View Issue Details

IDProjectCategoryView StatusLast Update
0001492T99X171.00 SKB EagleSW Issuepublic2022-08-29 08:25
Reporter(ALTech) Younkwang Jung Assigned To(ALTech) Younkwang Jung Due Date
PriorityurgentSeveritys4-minorReproducibilityhave not tried
Status closedResolutionfixed 
Summary0001492: [Smart3][VoC] About Booting Fail issue (urgent , BTVV-4467)
DescriptionHi Kerwin

This VoC issue has been increasing since Saturday (June 11).

The log and contents are in the following jira.
https://jira.skbroadband.com/browse/BTVV-4467

First of all, the cause of the issue is not clear,
There is a pattern in which ANR occurs in common. (It is not clear whether it is the cause or the effect. )

================ ANR ===========
..
  at android.media.MediaDrm.native_setup(Native method)
  at android.media.MediaDrm.<init>(MediaDrm.java:247)
  at com.skb.btv.framework.BaseApplication.setWVDrmInfo(BaseApplication.java:66)
..
==============================

This issue is only occurring in FXN smart3 model.
In this case, please consider whether it will occur after updating to Android 9-> Android 10.

And Smart3 v15.532.20 version is FW released at the end of last year. the sudden occurrence of this issue may be related to Google updates.

Please quickly analyze (or confirm) the cause of this issue.

Thank you
YK.Jung
TagsNo tags attached.
Attach Tags

Users monitoring this issue

User List (ALTech) JunGyu Kim , (ALTech) SY Yoon , (SW) Jacky Chiang , (SW) Jason Ling

Activities

(ALTech) Younkwang Jung

2022-06-13 10:45

developer   ~0010154

Last edited: 2022-06-13 10:48

View 2 revisions

Hi Kerwin

Additionally, I attach the ANR file in JIRA.
Please check the log(ANR) as well. (ANR_#1.zip)

And I heard the following from SKB (SKB received the contents below from Google TAM )
Katniss updated on 10 June on Google (I don't think it has anything to do with this issue)
GmsCore and webview updates are in progress now.
If it's related, I think it's related to GmsCore.

Thank you
YK.Jung

ANR_#1.zip (26,796,239 bytes)

(ALTech) Younkwang Jung

2022-06-13 16:44

developer   ~0010162

Hi Kerwin

Is there any update?

the problem is that android.media.MediaDrm.<init>(MediaDrm.java:247) is not working properly.

I wrote down some suspicious things.
1) Is it related to the chrome cast error after OS upgrade (Android 9 -> 10)?
  The following logs are being observed in Issue STBs.
  Please check if the log below is related to this issue.
  As far as I know, this log can be solved by clearing the corresponding Google data.
./0130000037_20220612_4C_EB_BD_1E_19_F2/main.log:06-12 18:08:08.279 5857 5857 I chromium: [5857:5857:INFO:application_identification_settings.cc(1442)] Missing signature, return empty JWT
./0130000037_20220612_4C_EB_BD_1E_19_F2/main.log:06-12 18:08:08.279 5857 5857 W chromium: [5857:5857:WARNING:robot_account_auth_manager.cc(491)] JWT signing failed. Retrying after 5 secs.
./0130000037_20220612_4C_EB_BD_1E_19_F2/main.log:06-12 18:08:13.430 5857 6220 I chromium: [5857:6220:INFO:jni_client_auth_creds_mediadrm.cc(222)] JniRsaSigner::SignHash: failed to openSession.
./0130000037_20220612_4C_EB_BD_1E_19_F2/main.log:06-12 18:08:13.431 5857 5857 I chromium: [5857:5857:INFO:application_identification_settings.cc(1437)] CreateJwt completed; running all done signing

2) Is it a provision issue?
   Other manufacturers are using provision.
   I understand that the FXN Smart3 model does not use provision.
   
3) Is it caused by Keymaster TA Fail?
./0130000037_20220612_1C_BF_C0_F9_0D_7E/dmesg.txt:[3528944.016841] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (err): keymaster/key_blob_utils/ocb_utils.cpp, Line 186: Failed to validate authentication tag during key decryption
./0130000037_20220612_1C_BF_C0_F9_0D_7E/dmesg.txt:[3528944.016847] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (err): ./keymaster/include/keymaster/attestation_record.h, Line 248: Cannot open attestationdevidbox, return KM_ERROR_UNIMPLEMENTED
./0130000037_20220612_28_CD_C4_FA_93_52/dmesg.txt:[ 7.263990] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (info): app/ipc/keymaster_ipc.cpp, Line 963: Amlogic KEYMASTER 4.0! Build Time: Mar 30 2020 19:30:40 version: 345f6a5
./0130000037_20220612_28_CD_C4_FA_93_52/dmesg.txt:[ 59.491996] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (err): ./keymaster/include/keymaster/attestation_record.h, Line 248: Cannot open attestationdevidbox, return KM_ERROR_UNIMPLEMENTED
./0130000037_20220612_28_CD_C4_FA_93_52/dmesg.txt:[ 59.492001] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (err): keymaster/key_blob_utils/ocb_utils.cpp, Line 186: Failed to validate authentication tag during key decryption
./0130000037_20220612_28_CD_C4_FD_85_F2/dmesg.txt:[ 6.276202] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (info): app/ipc/keymaster_ipc.cpp, Line 963: Amlogic KEYMASTER 4.0! Build Time: Mar 30 2020 19:30:40 version: 345f6a5
./0130000037_20220612_28_CD_C4_FD_85_F2/dmesg.txt:[ 47.236248] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (err): keymaster/key_blob_utils/ocb_utils.cpp, Line 186: Failed to validate authentication tag during key decryption
./0130000037_20220612_28_CD_C4_FD_85_F2/dmesg.txt:[ 47.236253] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (err): ./keymaster/include/keymaster/attestation_record.h, Line 248: Cannot open attestationdevidbox, return KM_ERROR_UNIMPLEMENTED
./0130000037_20220612_28_CD_C4_FD_85_F2/main.log.1:06-12 10:22:49.540 3425 3425 E AmlogicKeymaster: Response of size 4 contained error code -33
./0130000037_20220612_28_CD_C4_FD_85_F2/main.log.1:06-12 10:22:49.540 3425 3425 E AmlogicKeymaster: Failed to send cmd 60 err: -33
./0130000037_20220612_28_CD_C4_FD_85_F2/system.log:06-12 10:22:49.658 3831 5119 I DropBoxManagerService: add tag=keymaster isTagEnabled=true flags=0x0
./0130000037_20220612_28_CD_C4_FE_36_0C/dmesg.txt:[14339328.642883] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (err): ./keymaster/include/keymaster/attestation_record.h, Line 248: Cannot open attestationdevidbox, return KM_ERROR_UNIMPLEMENTED
./0130000037_20220612_28_CD_C4_FE_36_0C/dmesg.txt:[14339328.642889] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (err): keymaster/key_blob_utils/ocb_utils.cpp, Line 186: Failed to validate authentication tag during key decryption
./0130000037_20220612_40_5B_D8_24_80_B7/dmesg.txt:[15314062.942692] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (err): ./keymaster/include/keymaster/attestation_record.h, Line 248: Cannot open attestationdevidbox, return KM_ERROR_UNIMPLEMENTED
./0130000037_20220612_40_5B_D8_24_80_B7/dmesg.txt:[15314062.942697] [TEE] MESSAGE: USER-TA:log_msg:68: KeymasterTA (err): keymaster/key_blob_utils/ocb_utils.cpp, Line 186: Failed to validate authentication tag during key decryption
./0130000037_20220612_40_5B_D8_24_80_B7/main.log.1:06-12 10:34:44.357 3425 3425 E AmlogicKeymaster: Response of size 4 contained error code -33
./0130000037_20220612_40_5B_D8_24_80_B7/main.log.1:06-12 10:34:44.357 3425 3425 E AmlogicKeymaster: Failed to send cmd 60 err: -33
=
4) Is this an issue that occurs when STB is turned on for a long time?
  0130000037_20220612_1C_BF_C0_F9_0D_7E 40 days, 20:53
    0130000037_20220612_28_CD_C4_FE_36_0C 166 days, 3 min
    0130000037_20220612_28_CD_C4_FE_CB_6C 91 days, 11:27
    0130000037_20220612_28_CD_C4_FC_F6_4A 51 days, 19:43
    0130000037_20220612_40_5B_D8_24_80_B7 177 days, 6:08
    0130000037_20220612_D8_12_65_D0_63_FE 99 days, 17:58
    0130000037_20220612_E8_6F_38_53_0C_D6 39 days, 15:30
      
Please let me know immediately if anything is confirmed

Thank you
YK.Jung

(SW) Kerwin Chen

2022-06-13 18:02

developer   ~0010163

Last edited: 2022-06-13 20:25

Hi YK,

I don't have idea so far.
From source code level, MediaDrm() is simply a constructor to prepare some variables for MediaSession.
There is no useful patch on Google AOSP git for MediaDrm.

On SKB JIRA, it says issue is gone after STB reboots.
I think it is NOT the same as old "mediashell" issue. (Android 9 -> 10, issue remains after STB reboots. User needs to clear Google data.)

For keymaster messages, it should be checked by AML since it is in TEE.

Thank you !

(ALTech) Younkwang Jung

2022-06-30 16:54

developer   ~0010278

Hi Kerwin

An analysis of the booting fail issue is in progress.
https://jira.skbroadband.com/browse/BPM-2559
https://jira.skbroadband.com/browse/BTVV-4467

There is currently no way to reproduce this issue, so SKB decided to add logs to MediaDrm plugin and distribute FW to users.
Please add a log to identify the cause in the DRM plugin code below.
https://bitbucket.skbroadband.com/projects/AMANDROIDQ/repos/platform-vendor-widevine/browse/libwvdrmengine/mediadrm?at=aml_orig_hs30

log point is
1) Check if it is binder issue or drm issue
2) If you need any other logs, please add them.
 
Thank you
YK.Jung

(SW) Kerwin Chen

2022-07-04 08:53

developer   ~0010299

Hi YK,

From my test, it seems the source files in 'mediadrm/src' is not used.
I've added some messages, but they are not shown in logcat.

So, I add messages in 'src_hidl' folder.
Below is the logcat messages while STB boots up.
Please check if it is enough.

I also attach modified files for your reference.
Thank you !

=============================== BTVV-4467 logs ===================================
07-01 16:59:03.989 3509 3575 E WVCdm : [BTVV-4467] WVDrmPlugin,
07-01 16:59:04.378 3509 3509 E WVCdm : [BTVV-4467] ~WVDrmPlugin,
07-01 16:59:04.378 3509 3509 E WVCdm : [BTVV-4467] ~WVDrmPlugin, leave



07-01 16:59:13.108 3509 5163 E WVCdm : [BTVV-4467] WVDrmPlugin,
07-01 16:59:13.115 3509 5163 E WVCdm : [BTVV-4467] openSessionCommon,
07-01 16:59:13.204 3509 5163 E WVCdm : [BTVV-4467] openSessionCommon, calling to mCDM->OpenSession
07-01 16:59:13.294 3509 5163 E WVCdm : [BTVV-4467] openSessionCommon, Construct a CryptoSession
07-01 16:59:13.294 3509 5163 E WVCdm : [BTVV-4467] openSession,
07-01 16:59:13.294 3509 5163 E WVCdm : [BTVV-4467] openSession, leave
07-01 16:59:13.301 3509 5163 E WVCdm : [BTVV-4467] WVDrmPlugin,
07-01 16:59:13.303 3509 5163 E WVCdm : [BTVV-4467] getHdcpLevels_1_2,
07-01 16:59:13.310 3509 5163 E WVCdm : [BTVV-4467] signRSA,
07-01 16:59:13.310 3509 5163 E WVCdm : [BTVV-4467] signRSA, calling to OEMCrypto_OpenSession
07-01 16:59:13.311 3509 5163 E WVCdm : [BTVV-4467] signRSA, Load RSA key
07-01 16:59:13.311 3509 5163 E WVCdm : [BTVV-4467] signRSA, Generate RSA Signature
07-01 16:59:13.311 3509 5163 E WVCdm : [BTVV-4467] signRSA, Generate RSA Signature, resize
07-01 16:59:13.442 3509 5163 E WVCdm : [BTVV-4467] signRSA, close session, leave
07-01 16:59:13.456 3509 3509 E WVCdm : [BTVV-4467] ~WVDrmPlugin,
07-01 16:59:13.456 3509 3509 E WVCdm : [BTVV-4467] ~WVDrmPlugin, leave
07-01 16:59:13.871 3509 3509 E WVCdm : [BTVV-4467] WVDrmPlugin,
07-01 16:59:13.900 3509 3509 E WVCdm : [BTVV-4467] openSessionCommon,
07-01 16:59:14.000 3509 3509 E WVCdm : [BTVV-4467] openSessionCommon, calling to mCDM->OpenSession
07-01 16:59:14.100 3509 3509 E WVCdm : [BTVV-4467] openSessionCommon, Construct a CryptoSession
07-01 16:59:14.100 3509 3509 E WVCdm : [BTVV-4467] openSession,
07-01 16:59:14.100 3509 3509 E WVCdm : [BTVV-4467] openSession, leave
07-01 16:59:14.103 3509 3509 E WVCdm : [BTVV-4467] signRSA,
07-01 16:59:14.103 3509 3509 E WVCdm : [BTVV-4467] signRSA, calling to OEMCrypto_OpenSession
07-01 16:59:14.103 3509 3509 E WVCdm : [BTVV-4467] signRSA, Load RSA key
07-01 16:59:14.104 3509 3509 E WVCdm : [BTVV-4467] signRSA, Generate RSA Signature
07-01 16:59:14.104 3509 3509 E WVCdm : [BTVV-4467] signRSA, Generate RSA Signature, resize
07-01 16:59:14.172 3509 3509 E WVCdm : [BTVV-4467] signRSA, close session, leave07-01 16:59:03.989 3509 3575 E WVCdm : [BTVV-4467] WVDrmPlugin,
07-01 16:59:04.378 3509 3509 E WVCdm : [BTVV-4467] ~WVDrmPlugin,
07-01 16:59:04.378 3509 3509 E WVCdm : [BTVV-4467] ~WVDrmPlugin, leave
WVGenericCryptoInterface.cpp (2,216 bytes)   
//
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//

//#define LOG_NDEBUG 0
#define LOG_TAG "WVCdm"
#include <log/log.h>

#include "WVGenericCryptoInterface.h"

#include "wv_cdm_constants.h"

namespace wvdrm {

using namespace std;
using namespace wvcdm;

OEMCryptoResult WVGenericCryptoInterface::signRSA(const uint8_t* wrapped_rsa_key,
                                                  size_t wrapped_rsa_key_length,
                                                  const uint8_t* message,
                                                  size_t message_length,
                                                  std::vector<uint8_t>& signature,
                                                  RSA_Padding_Scheme padding_scheme) {
  OEMCrypto_SESSION session;
  ALOGE("[BTVV-4467] %s, calling to OEMCrypto_OpenSession", __FUNCTION__);
  OEMCryptoResult sts = OEMCrypto_OpenSession(&session);
  if (sts != OEMCrypto_SUCCESS) return sts;
  ALOGE("[BTVV-4467] %s, Load RSA key", __FUNCTION__);
  sts = OEMCrypto_LoadDeviceRSAKey(session, wrapped_rsa_key,
                                   wrapped_rsa_key_length);
  if (sts == OEMCrypto_SUCCESS) {
    size_t signatureSize = 0;
    ALOGE("[BTVV-4467] %s, Generate RSA Signature", __FUNCTION__);
    sts = OEMCrypto_GenerateRSASignature(session, message, message_length,
                                         NULL, &signatureSize,
                                         padding_scheme);
    if (sts == OEMCrypto_SUCCESS) {
      // Should be short buffer.
      sts = OEMCrypto_ERROR_UNKNOWN_FAILURE;
    } else if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
      signature.resize(signatureSize);
      ALOGE("[BTVV-4467] %s, Generate RSA Signature, resize", __FUNCTION__);
      sts = OEMCrypto_GenerateRSASignature(session, message, message_length,
                                           signature.data(), &signatureSize,
                                           padding_scheme);
    }
  }
  OEMCrypto_CloseSession(session);
  ALOGE("[BTVV-4467] %s, close session, leave", __FUNCTION__);
  return sts;
}

}  // namespace wvdrm
WVGenericCryptoInterface.cpp (2,216 bytes)   
WVDrmPlugin.cpp (72,445 bytes)   
//
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//

//#define LOG_NDEBUG 0
#define LOG_TAG "WVCdm"
#include <utils/Log.h>

#include <list>
#include <stdlib.h>

#include "WVDrmPlugin.h"

#include "android-base/macros.h"
#include "hidl_metrics_adapter.h"
#include "mapErrors-inl.h"
#include "media/stagefright/MediaErrors.h"
#include "metrics.pb.h"
#include "openssl/sha.h"
#include "wv_cdm_constants.h"
#include "TypeConvert.h"
#include "HidlTypes.h"

namespace {

static const char* const kResetSecurityLevel = "";
static const char* const kEnable = "enable";
static const char* const kDisable = "disable";
static const std::string kPsshTag = "pssh";
static const char* const kSpecialUnprovisionResponse = "unprovision";

}   // namespace

namespace wvdrm {
namespace hardware {
namespace drm {
namespace V1_2 {
namespace widevine {

using android::hardware::drm::V1_2::widevine::toHidlVec;
using android::hardware::drm::V1_2::widevine::toVector;
using wvcdm::kDefaultCdmIdentifier;
using wvcdm::CdmAppParameterMap;
using wvcdm::CdmCertificateType;
using wvcdm::CdmInitData;
using wvcdm::CdmKeyStatus;
using wvcdm::CdmKeyRequest;
using wvcdm::CdmKeyRequestType;
using wvcdm::CdmKeyResponse;
using wvcdm::CdmKeySetId;
using wvcdm::CdmLicenseType;
using wvcdm::CdmProvisioningRequest;
using wvcdm::CdmProvisioningResponse;
using wvcdm::CdmQueryMap;
using wvcdm::CdmSecureStopId;
using wvcdm::CdmSecurityLevel;
using wvcdm::CdmUsageInfo;
using wvcdm::CdmUsageInfoReleaseMessage;
using wvcdm::KeyId;

namespace {

std::vector<uint8_t> StrToVector(const std::string& str) {
  std::vector<uint8_t> vec(str.begin(), str.end());
  return vec;
}

KeyRequestType ConvertFromCdmKeyRequestType(
    CdmKeyRequestType keyRequestType) {
  switch (keyRequestType) {
    case wvcdm::kKeyRequestTypeInitial:
      return KeyRequestType::INITIAL;
    case wvcdm::kKeyRequestTypeRenewal:
      return KeyRequestType::RENEWAL;
    case wvcdm::kKeyRequestTypeRelease:
      return KeyRequestType::RELEASE;
    default:
      return KeyRequestType::UNKNOWN;
  }
}

KeyRequestType_V1_1 ConvertFromCdmKeyRequestType_1_1(
    CdmKeyRequestType keyRequestType) {
  switch (keyRequestType) {
    case wvcdm::kKeyRequestTypeNone:
      return KeyRequestType_V1_1::NONE;
    case wvcdm::kKeyRequestTypeUpdate:
      return KeyRequestType_V1_1::UPDATE;
    default:
      return static_cast<KeyRequestType_V1_1>(
          ConvertFromCdmKeyRequestType(keyRequestType));
  }
}

KeyRequestType toKeyRequestType_V1_0(KeyRequestType_V1_1 keyRequestType) {
  switch (keyRequestType) {
    case KeyRequestType_V1_1::NONE:
    case KeyRequestType_V1_1::UPDATE:
      return KeyRequestType::UNKNOWN;
    default:
      return static_cast<KeyRequestType>(keyRequestType);
  }
}

Status toStatus_1_0(Status_V1_2 status) {
  switch (status) {
    case Status_V1_2::ERROR_DRM_INSUFFICIENT_SECURITY:
    case Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE:
    case Status_V1_2::ERROR_DRM_SESSION_LOST_STATE:
    case Status_V1_2::ERROR_DRM_RESOURCE_CONTENTION:
      return Status::ERROR_DRM_UNKNOWN;
    default:
      return static_cast<Status>(status);
  }
}

template<typename KST>
KST ConvertFromCdmKeyStatus(CdmKeyStatus keyStatus);

template<>
KeyStatusType ConvertFromCdmKeyStatus<KeyStatusType>(CdmKeyStatus keyStatus) {
  switch (keyStatus) {
    case wvcdm::kKeyStatusUsable:
      return KeyStatusType::USABLE;
    case wvcdm::kKeyStatusExpired:
      return KeyStatusType::EXPIRED;
    case wvcdm::kKeyStatusOutputNotAllowed:
      return KeyStatusType::OUTPUTNOTALLOWED;
    case wvcdm::kKeyStatusPending:
    case wvcdm::kKeyStatusUsableInFuture:
      return KeyStatusType::STATUSPENDING;
    case wvcdm::kKeyStatusInternalError:
    default:
      return KeyStatusType::INTERNALERROR;
  }
}

template<>
KeyStatusType_V1_2 ConvertFromCdmKeyStatus<KeyStatusType_V1_2>(CdmKeyStatus keyStatus) {
  switch (keyStatus) {
    case wvcdm::kKeyStatusUsableInFuture:
      return KeyStatusType_V1_2::USABLEINFUTURE;
    default:
      return static_cast<KeyStatusType_V1_2>(ConvertFromCdmKeyStatus<KeyStatusType>(keyStatus));
  }
}

HdcpLevel mapHdcpLevel(const std::string& level) {
  if (level == wvcdm::QUERY_VALUE_HDCP_V1)
    return HdcpLevel::HDCP_V1;
  else if (level == wvcdm::QUERY_VALUE_HDCP_V2_0)
    return HdcpLevel::HDCP_V2;
  else if (level == wvcdm::QUERY_VALUE_HDCP_V2_1)
    return HdcpLevel::HDCP_V2_1;
  else if (level == wvcdm::QUERY_VALUE_HDCP_V2_2)
    return HdcpLevel::HDCP_V2_2;
  else if (level == wvcdm::QUERY_VALUE_HDCP_NONE)
    return HdcpLevel::HDCP_NONE;
  else if (level == wvcdm::QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT)
    return HdcpLevel::HDCP_NO_OUTPUT;
  else {
    ALOGE("Invalid HDCP level=%s", level.c_str());
    return HdcpLevel::HDCP_NONE;
  }
}

HdcpLevel_V1_2 mapHdcpLevel_1_2(const std::string level) {
    if (level == wvcdm::QUERY_VALUE_HDCP_V2_3) {
        return HdcpLevel_V1_2::HDCP_V2_3;
    }
    return static_cast<HdcpLevel_V1_2>(mapHdcpLevel(level));
}

HdcpLevel toHdcpLevel_1_1(HdcpLevel_V1_2 level) {
    if (level == HdcpLevel_V1_2::HDCP_V2_3) {
        return HdcpLevel::HDCP_NONE;
    }
    return static_cast<HdcpLevel>(level);
}

}  // namespace

WVDrmPlugin::WVDrmPlugin(const sp<WvContentDecryptionModule>& cdm,
                         const std::string& appPackageName,
                         WVGenericCryptoInterface* crypto,
                         bool useSpoid)
  : mCdmIdentifierBuilder(useSpoid, *this, appPackageName),
    mCDM(cdm),
    mCrypto(crypto),
    mCryptoSessions() { ALOGE("[BTVV-4467] %s, ", __FUNCTION__); }

WVDrmPlugin::~WVDrmPlugin() {
  typedef map<CdmSessionId, CryptoSession>::iterator mapIterator;
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  for (mapIterator iter = mCryptoSessions.begin();
       iter != mCryptoSessions.end();
       ++iter) {
    CdmResponseType res = mCDM->CloseSession(iter->first);
    if (!isCdmResponseTypeSuccess(res)) {
      ALOGE("Failed to close session while destroying WVDrmPlugin");
    }
  }
  mCryptoSessions.clear();
  if (mCdmIdentifierBuilder.is_sealed()) {
    CdmIdentifier identifier;
    Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
    if (status != Status::OK) {
      ALOGE("Failed to get cdm identifier %d", status);
    } else {
      status = mapCdmResponseType(mCDM->CloseCdm(identifier));
      if (status != Status::OK) {
        ALOGE("Failed to close cdm. status %d", status);
      }
    }
  }
  ALOGE("[BTVV-4467] %s, leave", __FUNCTION__);
}

Status WVDrmPlugin::openSessionCommon(std::vector<uint8_t>& sessionId) {
  Status status = Status::OK;

  CdmIdentifier identifier;
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    return status;
  }

  CdmSessionId cdmSessionId;
  ALOGE("[BTVV-4467] %s, calling to mCDM->OpenSession", __FUNCTION__);
  CdmResponseType res =
      mCDM->OpenSession("com.widevine", &mPropertySet, identifier, this,
                        &cdmSessionId);

  if (!isCdmResponseTypeSuccess(res)) {
    status = mapAndNotifyOfCdmResponseType(sessionId, res);
    return status;
  }

  bool success = false;

  // Construct a CryptoSession
  CdmQueryMap info;
  ALOGE("[BTVV-4467] %s, Construct a CryptoSession", __FUNCTION__);
  res = mCDM->QueryOemCryptoSessionId(cdmSessionId, &info);

  if (isCdmResponseTypeSuccess(res) &&
      info.count(wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID)) {
    OEMCrypto_SESSION oecSessionId;
    std::istringstream(
        info[wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID]) >> oecSessionId;
    mCryptoSessions[cdmSessionId] = CryptoSession(oecSessionId);
    success = true;
  } else {
    ALOGE("Unable to query key control info.");
  }

  if (success) {
    // Marshal Session ID
    sessionId = StrToVector(cdmSessionId);
    return Status::OK;
  } else {
    mCDM->CloseSession(cdmSessionId);

    if (!isCdmResponseTypeSuccess(res)) {
      // We got an error code we can return.
      status = mapAndNotifyOfCdmResponseType(sessionId, res);
    } else {
      // We got a failure that did not give us an error code, such as a failure
      // of AttachEventListener() or the key being missing from the map.
      ALOGW("Returns UNKNOWN error for legacy status kErrorCDMGeneric");
      status = Status::ERROR_DRM_UNKNOWN;
    }
  }

  ALOGE("[BTVV-4467] %s, leave", __FUNCTION__);
  return status;
}

Return<void> WVDrmPlugin::openSession(openSession_cb _hidl_cb) {
  std::vector<uint8_t> sessionId;
  Status status = openSessionCommon(sessionId);

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  _hidl_cb(status, toHidlVec(sessionId));
  ALOGE("[BTVV-4467] %s, leave", __FUNCTION__);
  return Void();
}

SecurityLevel WVDrmPlugin::mapSecurityLevel(const std::string& level) {
  SecurityLevel hSecurityLevel = SecurityLevel::UNKNOWN;

  if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1 == level) {
    hSecurityLevel = SecurityLevel::HW_SECURE_ALL;
  } else if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L2 == level) {
    hSecurityLevel = SecurityLevel::HW_SECURE_CRYPTO;
  } else if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3 == level) {
    hSecurityLevel = SecurityLevel::SW_SECURE_CRYPTO;
  } // else QUERY_VALUE_SECURITY_LEVEL_UNKNOWN returns Security::UNKNOWN

  return hSecurityLevel;
}

Return<void> WVDrmPlugin::openSession_1_1(
    SecurityLevel requestedLevel,
    openSession_1_1_cb _hidl_cb) {
  std::vector<uint8_t> sessionId;
  sessionId.clear();

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (SecurityLevel::UNKNOWN == requestedLevel) {
    _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, toHidlVec(sessionId));
    return Void();
  }

  std::string native_security_level;
  Status status = queryProperty(wvcdm::kLevelDefault,
          wvcdm::QUERY_KEY_SECURITY_LEVEL, native_security_level);
  if (Status::OK != status) {
    _hidl_cb(status, toHidlVec(sessionId));
    return Void();
  }

  if (wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3 == native_security_level &&
      requestedLevel >= SecurityLevel::SW_SECURE_DECODE) {
    _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, toHidlVec(sessionId));
    return Void();
  }

  std::string wvcdm_security_level =
      (SecurityLevel::SW_SECURE_CRYPTO == requestedLevel) ?
      wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3 : wvcdm::QUERY_VALUE_SECURITY_LEVEL_DEFAULT;

  setPropertyString("securityLevel", hidl_string(wvcdm_security_level));

  status = openSessionCommon(sessionId);
  hidl_vec<uint8_t> hSessionId = toHidlVec(sessionId);
  if (Status::OK == status) {
    Return<void> hResult = getSecurityLevel(hSessionId, [&](Status status, SecurityLevel hSecurityLevel) {
      if (Status::OK != status || requestedLevel != hSecurityLevel) {
        ALOGE("Failed to open session with the requested security level=%d", requestedLevel);
        if (Status::OK != closeSession(hSessionId)) sessionId.clear();
      }
    });
    if (!hResult.isOk()) {
      status = Status::ERROR_DRM_INVALID_STATE;
    }
  }
  _hidl_cb(status, toHidlVec(sessionId));
  ALOGE("[BTVV-4467] %s, leave", __FUNCTION__);
  return Void();
}

Return<Status> WVDrmPlugin::closeSession(const hidl_vec<uint8_t>& sessionId) {
  if (!sessionId.size()) {
    return Status::BAD_VALUE;
  }
  const std::vector<uint8_t> sId = toVector(sessionId);
  CdmSessionId cdmSessionId(sId.begin(), sId.end());
  CdmResponseType res = mCDM->CloseSession(cdmSessionId);
  mCryptoSessions.erase(cdmSessionId);
  if (!isCdmResponseTypeSuccess(res)) {
    return Status::ERROR_DRM_SESSION_NOT_OPENED;
  }
  return Status::OK;
}

Return<void> WVDrmPlugin::getKeyRequest(
    const hidl_vec<uint8_t>& scope,
    const hidl_vec<uint8_t>& initData,
    const hidl_string& mimeType,
    KeyType keyType,
    const hidl_vec<KeyValue>& optionalParameters,
    getKeyRequest_cb _hidl_cb) {
  hidl_string defaultUrl;
  hidl_vec<uint8_t> request;
  KeyRequestType requestType = KeyRequestType::UNKNOWN;
  Status status = Status::ERROR_DRM_UNKNOWN;

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  defaultUrl.clear();
  Return<void> hResult = getKeyRequest_1_1(
          scope, initData, mimeType, keyType, optionalParameters,
          [&](Status statusCode, const hidl_vec<uint8_t>& hRequest,
              KeyRequestType_V1_1 hKeyRequestType, const hidl_string& hDefaultUrl) {
            defaultUrl = hDefaultUrl;
            request = hRequest;
            requestType = toKeyRequestType_V1_0(hKeyRequestType);
            status = statusCode;
          });
  if (!hResult.isOk()) {
    status = Status::ERROR_DRM_INVALID_STATE;
  }
  _hidl_cb(status, request, requestType, defaultUrl);
  return Void();
}

Return<void> WVDrmPlugin::getKeyRequest_1_1(
    const hidl_vec<uint8_t>& scope,
    const hidl_vec<uint8_t>& initData,
    const hidl_string& mimeType,
    KeyType keyType,
    const hidl_vec<KeyValue>& optionalParameters,
    getKeyRequest_1_1_cb _hidl_cb) {
  hidl_string defaultUrl;
  hidl_vec<uint8_t> request;
  KeyRequestType_V1_1 requestType = KeyRequestType_V1_1::UNKNOWN;
  Status status = Status::ERROR_DRM_UNKNOWN;

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  defaultUrl.clear();
  Return<void> hResult = getKeyRequest_1_2(
      scope, initData, mimeType, keyType, optionalParameters,
          [&](Status_V1_2 statusCode, const hidl_vec<uint8_t>& hRequest,
              KeyRequestType_V1_1 hKeyRequestType, const hidl_string& hDefaultUrl) {
            defaultUrl = hDefaultUrl;
            request = hRequest;
            requestType = hKeyRequestType;
            status = toStatus_1_0(statusCode);
          });
  if (!hResult.isOk()) {
    status = Status::ERROR_DRM_INVALID_STATE;
  }
  _hidl_cb(status, request, requestType, defaultUrl);
  return Void();
}

Return<void> WVDrmPlugin::getKeyRequest_1_2(
    const hidl_vec<uint8_t>& scope,
    const hidl_vec<uint8_t>& initData,
    const hidl_string& mimeType,
    KeyType keyType,
    const hidl_vec<KeyValue>& optionalParameters,
    getKeyRequest_1_2_cb _hidl_cb) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (!scope.size()) {
    _hidl_cb(Status_V1_2::BAD_VALUE, hidl_vec<uint8_t>(),
        KeyRequestType_V1_1::UNKNOWN, "");
    return Void();
  }
  KeyRequestType_V1_1 requestType = KeyRequestType_V1_1::UNKNOWN;
  Status_V1_2 status = Status_V1_2::OK;
  std::string defaultUrl;
  std::vector<uint8_t> request;
  const std::vector<uint8_t> scopeId = toVector(scope);

  CdmIdentifier identifier;
  status = static_cast<Status_V1_2>(
      mCdmIdentifierBuilder.getCdmIdentifier(&identifier));
  if (status != Status_V1_2::OK) {
    _hidl_cb(status, toHidlVec(request), requestType,
             defaultUrl.c_str());
    return Void();
  }

  CdmLicenseType cdmLicenseType;
  CdmSessionId cdmSessionId;
  CdmKeySetId cdmKeySetId;
  if (keyType == KeyType::OFFLINE) {
    cdmLicenseType = wvcdm::kLicenseTypeOffline;
    cdmSessionId.assign(scopeId.begin(), scopeId.end());
  } else if (keyType == KeyType::STREAMING) {
    cdmLicenseType = wvcdm::kLicenseTypeStreaming;
    cdmSessionId.assign(scopeId.begin(), scopeId.end());
  } else if (keyType == KeyType::RELEASE) {
    cdmLicenseType = wvcdm::kLicenseTypeRelease;
    cdmKeySetId.assign(scopeId.begin(), scopeId.end());
  } else {
    _hidl_cb(Status_V1_2::BAD_VALUE, toHidlVec(request), KeyRequestType_V1_1::UNKNOWN,
             defaultUrl.c_str());
    return Void();
  }

  std::string cdmInitDataType = mimeType;
  // Provide backwards-compatibility for apps that pass non-EME-compatible MIME
  // types.
  if (!WvContentDecryptionModule::IsSupported(cdmInitDataType)) {
    cdmInitDataType = wvcdm::ISO_BMFF_VIDEO_MIME_TYPE;
  }

  CdmInitData processedInitData;
  if (initData.size() > 0 &&
      WvContentDecryptionModule::IsCenc(cdmInitDataType) &&
      !initDataResemblesPSSH(toVector(initData))) {
    // This data was passed in the old format, pre-unwrapped. We need to wrap
    // the init data in a new PSSH header.
    static const uint8_t psshPrefix[] = {
      0, 0, 0, 0,                                     // Total size
      'p', 's', 's', 'h',                             // "PSSH"
      0, 0, 0, 0,                                     // Flags - must be zero
      0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, // Widevine UUID
      0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
      0, 0, 0, 0                                      // Size of initData
    };
    processedInitData.assign(reinterpret_cast<const char*>(psshPrefix),
                             sizeof(psshPrefix) / sizeof(uint8_t));
    processedInitData.append(reinterpret_cast<const char*>(initData.data()),
                             initData.size());
    const size_t kPsshBoxSizeLocation = 0;
    const size_t kInitDataSizeLocation =
        sizeof(psshPrefix) - sizeof(uint32_t);
    uint32_t psshBoxSize = htonl(processedInitData.size());
    uint32_t initDataSize = htonl(initData.size());
    memcpy(&processedInitData[kPsshBoxSizeLocation], &psshBoxSize,
           sizeof(uint32_t));
    memcpy(&processedInitData[kInitDataSizeLocation], &initDataSize,
           sizeof(uint32_t));
  } else {
    // For other formats, we can pass the init data through unmodified.
    processedInitData.assign(reinterpret_cast<const char*>(initData.data()),
                             initData.size());
  }

  CdmAppParameterMap cdmParameters;
  for (size_t i = 0; i < optionalParameters.size(); ++i) {
    const std::string& key(optionalParameters[i].key);
    const std::string& value(optionalParameters[i].value);

    std::string cdmKey(key.c_str(), key.size());
    std::string cdmValue(value.c_str(), value.size());

    cdmParameters[cdmKey] = cdmValue;
  }

  CdmKeyRequest keyRequest;
  CdmResponseType res = mCDM->GenerateKeyRequest(
      cdmSessionId, cdmKeySetId, cdmInitDataType, processedInitData,
      cdmLicenseType, cdmParameters, &mPropertySet, identifier, &keyRequest);

  requestType = ConvertFromCdmKeyRequestType_1_1(keyRequest.type);

  if (isCdmResponseTypeSuccess(res)) {
    defaultUrl.clear();
    defaultUrl.assign(keyRequest.url.data(), keyRequest.url.size());

    request = StrToVector(keyRequest.message);
  }

  if (keyType == KeyType::RELEASE) {
    // When releasing keys, we do not have a session ID.
    status = mapCdmResponseType_1_2(res);
  } else {
    // For all other requests, we have a session ID.
    status = mapAndNotifyOfCdmResponseType_1_2(scopeId, res);
  }
  _hidl_cb(status, toHidlVec(request), requestType,
           defaultUrl.c_str());
  ALOGE("[BTVV-4467] %s, leave", __FUNCTION__);
  return Void();
}

Return<void> WVDrmPlugin::provideKeyResponse(
    const hidl_vec<uint8_t>& scope,
    const hidl_vec<uint8_t>& response,
    provideKeyResponse_cb _hidl_cb) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (scope.size() == 0 || response.size() == 0) {
    _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>());
    return Void();
  }
  const std::vector<uint8_t> resp = toVector(response);
  const std::vector<uint8_t> scopeId = toVector(scope);

  CdmKeySetId cdmKeySetId;
  CdmSessionId cdmSessionId;
  CdmKeyResponse cdmResponse(resp.begin(), resp.end());

  bool isRequest = (memcmp(scopeId.data(), wvcdm::SESSION_ID_PREFIX,
                           sizeof(wvcdm::SESSION_ID_PREFIX) - 1) == 0);
  bool isRelease = (memcmp(scopeId.data(), wvcdm::KEY_SET_ID_PREFIX,
                           sizeof(wvcdm::KEY_SET_ID_PREFIX) - 1) == 0);

  std::vector<uint8_t> keySetId;

  if (isRequest) {
    cdmSessionId.assign(scopeId.begin(), scopeId.end());
  } else if (isRelease) {
    cdmKeySetId.assign(scopeId.begin(), scopeId.end());
  } else {
    _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, toHidlVec(keySetId));
    return Void();
  }

  CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse, &cdmKeySetId);

  if (isRequest && isCdmResponseTypeSuccess(res)) {
    keySetId = StrToVector(cdmKeySetId);
  }

  Status status = Status::OK;
  if (isRelease) {
    // When releasing keys, we do not have a session ID.
    status = mapCdmResponseType(res);
  } else {
    // For all other requests, we have a session ID.
    status = mapAndNotifyOfCdmResponseType(scopeId, res);
    // For "NEED_KEY," we still want to send the notifcation, but then we don't
    // return the error.  This is because "NEED_KEY" from AddKey() is an
    // expected behavior when sending a privacy certificate.
    if (res == wvcdm::NEED_KEY && mPropertySet.use_privacy_mode()) {
      status = Status::OK;
    }
  }
  _hidl_cb(status, toHidlVec(keySetId));
  ALOGE("[BTVV-4467] %s, leave", __FUNCTION__);
  return Void();
}

Return<Status> WVDrmPlugin::removeKeys(const hidl_vec<uint8_t>& sessionId) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (!sessionId.size()) {
    return Status::BAD_VALUE;
  }
  const std::vector<uint8_t> sId = toVector(sessionId);
  CdmSessionId cdmSessionId(sId.begin(), sId.end());

  CdmResponseType res = mCDM->RemoveKeys(cdmSessionId);

  return mapAndNotifyOfCdmResponseType(sId, res);
}

Return<Status> WVDrmPlugin::restoreKeys(const hidl_vec<uint8_t>& sessionId,
                                        const hidl_vec<uint8_t>& keySetId) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (!sessionId.size() || !keySetId.size()) {
    return Status::BAD_VALUE;
  }
  const std::vector<uint8_t> kId = toVector(keySetId);
  const std::vector<uint8_t> sId = toVector(sessionId);
  CdmSessionId cdmSessionId(sId.begin(), sId.end());
  CdmKeySetId cdmKeySetId(kId.begin(), kId.end());

  CdmResponseType res = mCDM->RestoreKey(cdmSessionId, cdmKeySetId);

  return mapAndNotifyOfCdmResponseType(sId, res);
}

 Return<void> WVDrmPlugin::queryKeyStatus(const hidl_vec<uint8_t>& sessionId,
                                          queryKeyStatus_cb _hidl_cb) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (sessionId.size() == 0) {
    _hidl_cb(Status::BAD_VALUE, hidl_vec<KeyValue>());
    return Void();
  }
  const std::vector<uint8_t> sId = toVector(sessionId);
  CdmSessionId cdmSessionId(sId.begin(), sId.end());
  CdmQueryMap cdmLicenseInfo;

  CdmResponseType res = mCDM->QueryKeyStatus(cdmSessionId, &cdmLicenseInfo);

  std::vector<KeyValue> infoMapVec;
  if (isCdmResponseTypeSuccess(res)) {
    infoMapVec.clear();

    KeyValue keyValuePair;
    for (CdmQueryMap::const_iterator iter = cdmLicenseInfo.begin();
         iter != cdmLicenseInfo.end();
         ++iter) {
      const std::string& cdmKey = iter->first;
      const std::string& cdmValue = iter->second;
      keyValuePair.key = std::string(cdmKey.data(), cdmKey.size());
      keyValuePair.value = std::string(cdmValue.data(), cdmValue.size());
      infoMapVec.push_back(keyValuePair);
    }
  }

  _hidl_cb(mapCdmResponseType(res), toHidlVec(infoMapVec));
  return Void();
}

Return<void> WVDrmPlugin::getProvisionRequest(
     const hidl_string& certificateType,
     const hidl_string& certificateAuthority,
     getProvisionRequest_cb _hidl_cb) {
  Status status = Status::OK;
  std::string defaultUrl;
  std::vector<uint8_t> request;

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  Return<void> hResult = getProvisionRequest_1_2(
      certificateType, certificateAuthority,
          [&](Status_V1_2 statusCode, const hidl_vec<uint8_t>& hRequest,
              const hidl_string& hDefaultUrl) {
            request = hRequest;
            status = toStatus_1_0(statusCode);
            defaultUrl = hDefaultUrl;
          });
  if (!hResult.isOk()) {
    status = Status::ERROR_DRM_INVALID_STATE;
  }

  _hidl_cb(status, toHidlVec(request), hidl_string(defaultUrl));
  return Void();
}

Return<void> WVDrmPlugin::getProvisionRequest_1_2(
     const hidl_string& certificateType,
     const hidl_string& certificateAuthority,
     getProvisionRequest_1_2_cb _hidl_cb) {
  Status_V1_2 status = Status_V1_2::OK;
  std::string defaultUrl;
  std::vector<uint8_t> request;

  CdmIdentifier identifier;
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  status = static_cast<Status_V1_2>(mCdmIdentifierBuilder.getCdmIdentifier(&identifier));
  if (status != Status_V1_2::OK) {
    _hidl_cb(status, toHidlVec(request), hidl_string(defaultUrl));
    return Void();
  }

  CdmProvisioningRequest cdmProvisionRequest;
  std::string cdmDefaultUrl;

  CdmCertificateType cdmCertType = wvcdm::kCertificateWidevine;
  if (certificateType == "X.509") {
    cdmCertType = wvcdm::kCertificateX509;
  }

  std::string cdmCertAuthority = certificateAuthority;

  CdmResponseType res = mCDM->GetProvisioningRequest(
      cdmCertType, cdmCertAuthority, identifier,
      mProvisioningServiceCertificate, &cdmProvisionRequest, &cdmDefaultUrl);
  if (isCdmResponseTypeSuccess(res)) {
    request = StrToVector(cdmProvisionRequest);
    defaultUrl.clear();
    defaultUrl.assign(cdmDefaultUrl.data(), cdmDefaultUrl.size());
  }

  _hidl_cb(mapCdmResponseType_1_2(res), toHidlVec(request),
           hidl_string(defaultUrl));
  return Void();
}

Return<void> WVDrmPlugin::provideProvisionResponse(
    const hidl_vec<uint8_t>& response,
    provideProvisionResponse_cb _hidl_cb) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (!response.size()) {
    _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>(), hidl_vec<uint8_t>());
    return Void();
  }
  const std::vector<uint8_t> resp = toVector(response);
  std::vector<uint8_t> certificate;
  std::vector<uint8_t> wrappedKey;

  CdmIdentifier identifier;
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    _hidl_cb(status, toHidlVec(certificate), toHidlVec(wrappedKey));
  }

  CdmProvisioningResponse cdmResponse(resp.begin(), resp.end());
  if (cdmResponse == kSpecialUnprovisionResponse) {
    if (identifier.IsEquivalentToDefault()) {
       ALOGW("Returns UNKNOWN error for legacy status kErrorNoOriginSpecified");
      _hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(certificate),
               toHidlVec(wrappedKey));
      return Void();
    }
    _hidl_cb(unprovision(identifier),
             toHidlVec(certificate),
             toHidlVec(wrappedKey));
    return Void();
  } else {
    std::string cdmCertificate;
    std::string cdmWrappedKey;
    CdmResponseType res = mCDM->HandleProvisioningResponse(
        identifier, cdmResponse, &cdmCertificate, &cdmWrappedKey);
    if (isCdmResponseTypeSuccess(res)) {
      certificate = StrToVector(cdmCertificate);
      wrappedKey = StrToVector(cdmWrappedKey);
    }

    _hidl_cb(mapCdmResponseType(res), toHidlVec(certificate),
             toHidlVec(wrappedKey));
    return Void();
  }
}

Status WVDrmPlugin::unprovisionDevice() {
  return unprovision(kDefaultCdmIdentifier);
}

Return<void> WVDrmPlugin::getSecureStop(
    const hidl_vec<uint8_t>& secureStopId,
    getSecureStop_cb _hidl_cb) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (!secureStopId.size()) {
    _hidl_cb(Status::BAD_VALUE, SecureStop());
    return Void();
  }

  const std::vector<uint8_t> id = toVector(secureStopId);
  std::vector<uint8_t> cdmStopVec;
  SecureStop secureStop;

  CdmIdentifier identifier;
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    _hidl_cb(status, secureStop);
    return Void();
  }

  CdmUsageInfo cdmUsageInfo;
  CdmSecureStopId cdmSsId(id.begin(), id.end());
  CdmResponseType res = mCDM->GetUsageInfo(
      mPropertySet.app_id(), cdmSsId, identifier, &cdmUsageInfo);

  if (isCdmResponseTypeSuccess(res)) {
    for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin();
         iter != cdmUsageInfo.end();
         ++iter) {
      const std::string& cdmStop = *iter;
      cdmStopVec = StrToVector(cdmStop);
    }
    secureStop.opaqueData = toHidlVec(cdmStopVec);
  }

  _hidl_cb(mapCdmResponseType(res), secureStop);
  return Void();
}

Return<void> WVDrmPlugin::getSecureStops(getSecureStops_cb _hidl_cb) {

  std::list<std::vector<uint8_t> > secureStops;
  std::vector<SecureStop> secureStopsVec;

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  CdmIdentifier identifier;
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    _hidl_cb(status, toHidlVec(secureStopsVec));
    return Void();
  }

  CdmUsageInfo cdmUsageInfo;
  CdmResponseType res =
      mCDM->GetUsageInfo(mPropertySet.app_id(), identifier, &cdmUsageInfo);

  if (isCdmResponseTypeSuccess(res)) {
    secureStops.clear();
    for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin();
         iter != cdmUsageInfo.end();
         ++iter) {
      const std::string& cdmStop = *iter;
      secureStops.push_back(StrToVector(cdmStop));
    }
  }

  std::list<std::vector<uint8_t> >::iterator iter = secureStops.begin();
  while (iter != secureStops.end()) {
    SecureStop secureStop;
    secureStop.opaqueData = toHidlVec(*iter++);
    secureStopsVec.push_back(secureStop);
  }

  _hidl_cb(mapCdmResponseType(res), toHidlVec(secureStopsVec));
  return Void();
}

Return<Status> WVDrmPlugin::releaseAllSecureStops() {
  return removeAllSecureStops();
}

Return<Status> WVDrmPlugin::releaseSecureStop(
    const hidl_vec<uint8_t>& secureStopId) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (!secureStopId.size()) {
    return Status::BAD_VALUE;
  }

  CdmIdentifier identifier;
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    return status;
  }

  const std::vector<uint8_t> ssRelease = toVector(secureStopId);
  CdmUsageInfoReleaseMessage cdmMessage(ssRelease.begin(), ssRelease.end());
  CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage, identifier);
  return mapCdmResponseType(res);
}

Return<void> WVDrmPlugin::getMetrics(getMetrics_cb _hidl_cb) {
  hidl_vec<DrmMetricGroup> hidl_metrics;
  CdmIdentifier identifier;
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    _hidl_cb(status, hidl_metrics);
    return Void();
  }

  drm_metrics::WvCdmMetrics proto_metrics;
  CdmResponseType result = mCDM->GetMetrics(identifier, &proto_metrics);
  if (result != wvcdm::NO_ERROR) {
    status = mapCdmResponseType(result);
    _hidl_cb(status, hidl_metrics);
    return Void();
  }

  ::wvcdm::HidlMetricsAdapter adapter;
  ::wvcdm::HidlMetricsAdapter::ToHidlMetrics(proto_metrics, &hidl_metrics);
  _hidl_cb(Status::OK, hidl_metrics);
  return Void();
}

Return<void> WVDrmPlugin::getSecureStopIds(getSecureStopIds_cb _hidl_cb) {

  std::vector<SecureStopId> secureStopIds;

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  CdmIdentifier identifier;
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    _hidl_cb(status, toHidlVec(secureStopIds));
    return Void();
  }

  std::vector<CdmSecureStopId> ssids;
  CdmResponseType res =
      mCDM->GetSecureStopIds(mPropertySet.app_id(), identifier, &ssids);

  if (isCdmResponseTypeSuccess(res)) {
    for (auto itr = ssids.begin(); itr != ssids.end(); ++itr) {
      const CdmSecureStopId& ssid = *itr;
      secureStopIds.push_back(StrToVector(ssid));
    }
  }

  _hidl_cb(mapCdmResponseType(res), toHidlVec(secureStopIds));
  return Void();
}

Return<Status> WVDrmPlugin::releaseSecureStops(const SecureStopRelease& ssRelease) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (ssRelease.opaqueData.size() == 0) {
    return Status::BAD_VALUE;
  }

  CdmIdentifier identifier;
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    return status;
  }

  const std::vector<uint8_t> data = toVector(ssRelease.opaqueData);
  CdmUsageInfoReleaseMessage cdmMessage(data.begin(), data.end());
  CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage, identifier);
  return mapCdmResponseType(res);
}

Return<Status> WVDrmPlugin::removeSecureStop(const hidl_vec<uint8_t>& secureStopId) {
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (!secureStopId.size()) {
    return Status::BAD_VALUE;
  }

  CdmIdentifier identifier;
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    return status;
  }

  const std::vector<uint8_t> idVec = toVector(secureStopId);
  CdmSecureStopId id(idVec.begin(), idVec.end());
  CdmResponseType res = mCDM->RemoveUsageInfo(mPropertySet.app_id(), identifier, id);
  return mapCdmResponseType(res);
}

Return<Status> WVDrmPlugin::removeAllSecureStops() {
  CdmIdentifier identifier;
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    return status;
  }

  CdmResponseType res = mCDM->RemoveAllUsageInfo(mPropertySet.app_id(),
                                                 identifier);
  return mapCdmResponseType(res);
}

Return<void> WVDrmPlugin::getHdcpLevels(getHdcpLevels_cb _hidl_cb) {
    HdcpLevel connectedLevel = HdcpLevel::HDCP_NONE;
    HdcpLevel maxLevel = HdcpLevel::HDCP_NO_OUTPUT;

    ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
    Return<void> hResult = getHdcpLevels_1_2(
            [&](Status_V1_2 status, const HdcpLevel_V1_2& hConnected,
                               const HdcpLevel_V1_2& hMax) {
                if (status == Status_V1_2::OK) {
                    connectedLevel = toHdcpLevel_1_1(hConnected);
                    maxLevel = toHdcpLevel_1_1(hMax);
                }
            });

  _hidl_cb(Status::OK, connectedLevel, maxLevel);
  return Void();
}

Return<void> WVDrmPlugin::getHdcpLevels_1_2(getHdcpLevels_1_2_cb _hidl_cb) {
  HdcpLevel_V1_2 connectedLevel = HdcpLevel_V1_2::HDCP_NONE;
  HdcpLevel_V1_2 maxLevel = HdcpLevel_V1_2::HDCP_NO_OUTPUT;

  std::string level;
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  Status status = queryProperty(wvcdm::QUERY_KEY_CURRENT_HDCP_LEVEL, level);
  if (status == Status::OK) {
    connectedLevel = mapHdcpLevel_1_2(level);
  } else {
    ALOGE("Failed to query current hdcp level.");
    _hidl_cb(Status_V1_2::ERROR_DRM_INVALID_STATE, connectedLevel, maxLevel);
    return Void();
  }

  status = queryProperty(wvcdm::QUERY_KEY_MAX_HDCP_LEVEL, level);
  if (status == Status::OK) {
    maxLevel = mapHdcpLevel_1_2(level);
  } else {
    ALOGE("Failed to query maximum hdcp level.");
    _hidl_cb(Status_V1_2::ERROR_DRM_INVALID_STATE, connectedLevel, maxLevel);
    return Void();
  }

  _hidl_cb(Status_V1_2::OK, connectedLevel, maxLevel);
  return Void();
}

Return<void> WVDrmPlugin::getNumberOfSessions(getNumberOfSessions_cb _hidl_cb) {
  uint32_t currentSessions = 0;
  uint32_t maxSessions = 1;

  std::string value;
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  Status status = queryProperty(wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS, value);
  if (status == Status::OK) {
    currentSessions = std::strtoul(value.c_str(), nullptr, 10);
  } else {
    ALOGE("Failed to query currently opened sessions.");
    _hidl_cb(Status::ERROR_DRM_INVALID_STATE, currentSessions, maxSessions);
    return Void();
  }

  status = queryProperty(wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS, value);
  if (status == Status::OK) {
    maxSessions = std::strtoul(value.c_str(), nullptr, 10);
  } else {
    ALOGE("Failed to query maximum number of sessions that the device can support.");
    _hidl_cb(Status::ERROR_DRM_INVALID_STATE, currentSessions, maxSessions);
    return Void();
  }

  _hidl_cb(Status::OK, currentSessions, maxSessions);
  return Void();
}

Return<void> WVDrmPlugin::getSecurityLevel(
    const hidl_vec<uint8_t>& sessionId,
    getSecurityLevel_cb _hidl_cb) {
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (sessionId.size() == 0) {
    _hidl_cb(Status::BAD_VALUE, SecurityLevel::UNKNOWN);
    return Void();
  }

  std::vector<uint8_t> sid = toVector(sessionId);
  CdmQueryMap info;
  SecurityLevel hSecurityLevel = SecurityLevel::UNKNOWN;

  CdmResponseType status = mCDM->QuerySessionStatus(
          std::string(sid.begin(), sid.end()), &info);
  if (wvcdm::NO_ERROR == status) {
    std::string level = info[wvcdm::QUERY_KEY_SECURITY_LEVEL];
    hSecurityLevel = mapSecurityLevel(level);
  } else {
    ALOGE("Failed to query security level, status=%d", status);
  }

  _hidl_cb(mapCdmResponseType(status), hSecurityLevel);
  return Void();
}

Return<void> WVDrmPlugin::getOfflineLicenseKeySetIds(
    getOfflineLicenseKeySetIds_cb _hidl_cb) {
  std::vector<std::vector<uint8_t> > keySetIds;
  std::vector<KeySetId> keySetIdsVec;

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  CdmIdentifier identifier;
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    _hidl_cb(status, toHidlVec(keySetIdsVec));
    return Void();
  }

  std::vector<CdmSecurityLevel> levels = {
      wvcdm::kSecurityLevelL1, wvcdm::kSecurityLevelL3 };

  CdmResponseType res = wvcdm::UNKNOWN_ERROR;

  for (auto level : levels) {
    std::vector<CdmKeySetId> cdmKeySetIds;
    res = mCDM->ListStoredLicenses(level, identifier, &cdmKeySetIds);

    if (isCdmResponseTypeSuccess(res)) {
      keySetIds.clear();
      for (auto id : cdmKeySetIds) {
        keySetIds.push_back(StrToVector(id));
      }
      for (auto id : keySetIds) {
        keySetIdsVec.push_back(id);
      }
    }
  }
  _hidl_cb(mapCdmResponseType(res), toHidlVec(keySetIdsVec));
  return Void();
}

Return<void> WVDrmPlugin::getOfflineLicenseState(const KeySetId &keySetId,
    getOfflineLicenseState_cb _hidl_cb) {
    OfflineLicenseState licenseState = OfflineLicenseState::UNKNOWN;

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (!keySetId.size()) {
    _hidl_cb(Status::BAD_VALUE, licenseState);
    return Void();
  }

  CdmIdentifier identifier;
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    _hidl_cb(status, licenseState);
    return Void();
  }

  CdmResponseType res = wvcdm::UNKNOWN_ERROR;
  CdmKeySetId keySetIdStr(keySetId.begin(), keySetId.end());

  wvcdm::CdmOfflineLicenseState state = wvcdm::kLicenseStateUnknown;
  res = mCDM->GetOfflineLicenseState(
      keySetIdStr,
      wvcdm::kSecurityLevelL1,
      identifier,
      &state);
  if (!isCdmResponseTypeSuccess(res)) {
    // try L3
    res = mCDM->GetOfflineLicenseState(
        keySetIdStr,
        wvcdm::kSecurityLevelL3,
        identifier,
        &state);
    if (!isCdmResponseTypeSuccess(res)) {
      _hidl_cb(Status::BAD_VALUE, licenseState);
      return Void();
    }
  }

  switch(state) {
    case wvcdm::kLicenseStateActive:
      licenseState = OfflineLicenseState::USABLE;
      break;
    case wvcdm::kLicenseStateReleasing:
      licenseState = OfflineLicenseState::INACTIVE;
      break;
    default:
      licenseState = OfflineLicenseState::UNKNOWN;
      ALOGE("Return unknown offline license state for %s", keySetIdStr.c_str());
      break;
  }

  _hidl_cb(mapCdmResponseType(res), licenseState);
  return Void();
}

Return<Status> WVDrmPlugin::removeOfflineLicense(const KeySetId &keySetId) {
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (!keySetId.size()) {
    return Status::BAD_VALUE;
  }

  CdmIdentifier identifier;
  Status status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
  if (status != Status::OK) {
    return status;
  }

  CdmResponseType res = wvcdm::UNKNOWN_ERROR;

  res = mCDM->RemoveOfflineLicense(
      std::string(keySetId.begin(), keySetId.end()),
      wvcdm::kSecurityLevelL1,
      identifier);
  if (!isCdmResponseTypeSuccess(res)) {
    CdmResponseType res = mCDM->RemoveOfflineLicense(
        std::string(keySetId.begin(), keySetId.end()),
        wvcdm::kSecurityLevelL3,
        identifier);
    status = mapCdmResponseType(res);
  }

  return status;
}

Return<void> WVDrmPlugin::getPropertyString(const hidl_string& propertyName,
                                            getPropertyString_cb _hidl_cb) {
  Status status = Status::OK;
  std::string name(propertyName.c_str());
  std::string value;

  if (name == "vendor") {
    value = "Google";
  } else if (name == "version") {
    status = queryProperty(wvcdm::QUERY_KEY_WVCDM_VERSION, value);
  } else if (name == "description") {
    value = "Widevine CDM";
  } else if (name == "algorithms") {
    value = "AES/CBC/NoPadding,HmacSHA256";
  } else if (name == "securityLevel") {
    std::string requestedLevel = mPropertySet.security_level();

    if (requestedLevel.length() > 0) {
      value = requestedLevel.c_str();
    } else {
      status = queryProperty(wvcdm::QUERY_KEY_SECURITY_LEVEL, value);
    }
  } else if (name == "systemId") {
    status = queryProperty(wvcdm::QUERY_KEY_SYSTEM_ID, value);
  } else if (name == "privacyMode") {
    if (mPropertySet.use_privacy_mode()) {
      value = kEnable;
    } else {
      value = kDisable;
    }
  } else if (name == "sessionSharing") {
    if (mPropertySet.is_session_sharing_enabled()) {
      value = kEnable;
    } else {
      value = kDisable;
    }
  } else if (name == "hdcpLevel") {
    status = queryProperty(wvcdm::QUERY_KEY_CURRENT_HDCP_LEVEL, value);
  } else if (name == "maxHdcpLevel") {
    status = queryProperty(wvcdm::QUERY_KEY_MAX_HDCP_LEVEL, value);
  } else if (name == "usageReportingSupport") {
    status = queryProperty(wvcdm::QUERY_KEY_USAGE_SUPPORT, value);
  } else if (name == "numberOfOpenSessions") {
    status = queryProperty(wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS, value);
  } else if (name == "maxNumberOfSessions") {
    status = queryProperty(wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS, value);
  } else if (name == "oemCryptoApiVersion") {
    status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION, value);
  } else if (name == "appId") {
    value = mPropertySet.app_id().c_str();
  } else if (name == "origin") {
    value = mCdmIdentifierBuilder.origin().c_str();
  } else if (name == "CurrentSRMVersion") {
    status = queryProperty(wvcdm::QUERY_KEY_CURRENT_SRM_VERSION, value);
  } else if (name == "SRMUpdateSupport") {
    status = queryProperty(wvcdm::QUERY_KEY_SRM_UPDATE_SUPPORT, value);
  } else if (name == "resourceRatingTier") {
    status = queryProperty(wvcdm::QUERY_KEY_RESOURCE_RATING_TIER, value);
  } else if (name == "oemCryptoBuildInformation") {
    status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, value);
  } else if (name == "decryptHashSupport") {
    status = queryProperty(wvcdm::QUERY_KEY_DECRYPT_HASH_SUPPORT, value);
  } else if (name == "decryptHashError") {
    status = mapCdmResponseType(
        mCDM->GetDecryptHashError(mDecryptHashSessionId, &value));
  } else {
    ALOGE("App requested unknown string property %s", name.c_str());
    status = Status::ERROR_DRM_CANNOT_HANDLE;
  }

  _hidl_cb(status, value.c_str());
  return Void();
}

Return<void> WVDrmPlugin::getPropertyByteArray(
    const hidl_string& propertyName,
    getPropertyByteArray_cb _hidl_cb) {

  Status status = Status::OK;
  std::string name(propertyName.c_str());
  std::vector<uint8_t> value;

  if (name == "deviceUniqueId") {
    std::string id;
    status = mCdmIdentifierBuilder.getDeviceUniqueId(&id);
    if (status == Status::OK) {
      value = StrToVector(id);
    }
  } else if (name == "provisioningUniqueId") {
    std::string id;
    status = mCdmIdentifierBuilder.getProvisioningUniqueId(&id);
    if (status == Status::OK) {
      value = StrToVector(id);
    }
  } else if (name == "serviceCertificate") {
    value = StrToVector(mPropertySet.service_certificate());
  } else if (name == "provisioningServiceCertificate") {
    value = StrToVector(mProvisioningServiceCertificate);
  } else if (name == "metrics") {
    drm_metrics::WvCdmMetrics metrics;
    // If the cdm identifier is not yet sealed, then there are no metrics
    // for that cdm engine. Avoid calling getCdmIdentifier and sealing
    // the identifier builder.
    if (mCdmIdentifierBuilder.is_sealed()) {
      CdmIdentifier identifier;
      status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
      if (status != Status::OK) {
        ALOGE("Unexpected error retrieving cdm identifier: %d", status);
      } else {
        status = mapCdmResponseType(mCDM->GetMetrics(identifier, &metrics));
      }
    }
    if (status == Status::OK) {
      std::string serialized_metrics;
      if (!metrics.SerializeToString(&serialized_metrics)) {
        status = Status::ERROR_DRM_UNKNOWN;
      } else {
        value = StrToVector(serialized_metrics);
      }
    }
  } else {
    ALOGE("App requested unknown byte array property %s", name.c_str());
    status = Status::ERROR_DRM_CANNOT_HANDLE;
  }

  _hidl_cb(status, toHidlVec(value));
  return Void();
}

Return<Status> WVDrmPlugin::setPropertyString(const hidl_string& propertyName,
                                              const hidl_string& value) {
  std::string name(propertyName.c_str());
  std::string _value(value.c_str());

  if (name == "securityLevel") {
    if (mCryptoSessions.size() == 0) {
      if (_value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3.c_str()) {
        mPropertySet.set_security_level(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3);
      } else if (_value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1.c_str()) {
        // We must be sure we CAN set the security level to L1.
        std::string current_security_level;
        Status status = queryProperty(
            wvcdm::kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL,
            current_security_level);
        if (status != Status::OK) {
           return status;
        }
        if (current_security_level != wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) {
          ALOGE("App requested L1 security on a non-L1 device.");
          return Status::BAD_VALUE;
        } else {
          mPropertySet.set_security_level(kResetSecurityLevel);
        }
      } else if (_value == kResetSecurityLevel ||
                 _value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_DEFAULT) {
        mPropertySet.set_security_level(kResetSecurityLevel);
      } else {
        ALOGE("App requested invalid security level %s", _value.c_str());
        return Status::BAD_VALUE;
      }
    } else {
      ALOGE("App tried to change security level while sessions are open.");
      ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
      return Status::ERROR_DRM_UNKNOWN;
    }
  } else if (name == "privacyMode") {
    if (_value == kEnable) {
      mPropertySet.set_use_privacy_mode(true);
    } else if (_value == kDisable) {
      mPropertySet.set_use_privacy_mode(false);
    } else {
      ALOGE("App requested unknown privacy mode %s", _value.c_str());
      return Status::BAD_VALUE;
    }
  } else if (name == "sessionSharing") {
    if (mCryptoSessions.size() == 0) {
      if (_value == kEnable) {
        mPropertySet.set_is_session_sharing_enabled(true);
      } else if (_value == kDisable) {
        mPropertySet.set_is_session_sharing_enabled(false);
      } else {
        ALOGE("App requested unknown sharing type %s", _value.c_str());
        return Status::BAD_VALUE;
      }
    } else {
      ALOGE("App tried to change key sharing while sessions are open.");
      ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
      return Status::ERROR_DRM_UNKNOWN;
    }
  } else if (name == "appId") {
    if (mCryptoSessions.size() == 0) {
      mPropertySet.set_app_id(_value.c_str());
    } else {
      ALOGE("App tried to set the application id while sessions are opened.");
      ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
      return Status::ERROR_DRM_UNKNOWN;
    }
  } else if (name == "origin") {
    if (mCryptoSessions.size() != 0) {
      ALOGE("App tried to set the origin while sessions are opened.");
      ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
      return Status::ERROR_DRM_UNKNOWN;
    } else {
      if (!mCdmIdentifierBuilder.set_origin(_value.c_str())) {
        return Status::BAD_VALUE;
      }
    }
  } else if (name == "decryptHash") {
    wvcdm::CdmSessionId sessionId;
    CdmResponseType res =
      mCDM->SetDecryptHash(_value.c_str(), &sessionId);

    if (wvcdm::NO_ERROR == res) mDecryptHashSessionId = sessionId;

    return mapCdmResponseType(res);
  } else if (name == "decryptHashSessionId") {
    mDecryptHashSessionId = _value.c_str();
  } else {
    ALOGE("App set unknown string property %s", name.c_str());
    return Status::ERROR_DRM_CANNOT_HANDLE;
  }

  return Status::OK;
}

Return<Status> WVDrmPlugin::setPropertyByteArray(
    const hidl_string& propertyName, const hidl_vec<uint8_t>& value) {

  std::string name(propertyName.c_str());
  std::vector<uint8_t> _value = toVector(value);

  if (name == "serviceCertificate") {
    std::string cert(_value.begin(), _value.end());
    if (_value.empty() || mCDM->IsValidServiceCertificate(cert)) {
      mPropertySet.set_service_certificate(cert);
    } else {
      return Status::BAD_VALUE;
    }
  } else if (name == "provisioningServiceCertificate") {
    std::string cert(_value.begin(), _value.end());
    if (_value.empty() || mCDM->IsValidServiceCertificate(cert)) {
      mProvisioningServiceCertificate = cert;
    } else {
      return Status::BAD_VALUE;
    }
  } else {
    ALOGE("App set unknown byte array property %s", name.c_str());
    return Status::ERROR_DRM_CANNOT_HANDLE;
  }

  return Status::OK;
}

Return<Status> WVDrmPlugin::setCipherAlgorithm(
    const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (sessionId.size() == 0 || algorithm.size() == 0) {
    return Status::BAD_VALUE;
  }
  std::string algo(algorithm.c_str());
  std::vector<uint8_t> sId = toVector(sessionId);

  CdmSessionId cdmSessionId(sId.begin(), sId.end());
  if (!mCryptoSessions.count(cdmSessionId)) {
    return Status::ERROR_DRM_SESSION_NOT_OPENED;
  }

  CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];

  if (algo == "AES/CBC/NoPadding") {
    cryptoSession.setCipherAlgorithm(OEMCrypto_AES_CBC_128_NO_PADDING);
  } else {
    return Status::ERROR_DRM_CANNOT_HANDLE;
  }

  return Status::OK;
}

Return<Status> WVDrmPlugin::setMacAlgorithm(
    const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (sessionId.size() == 0 || algorithm.size() == 0) {
    return Status::BAD_VALUE;
  }
  std::string algo(algorithm.c_str());
  std::vector<uint8_t> sId = toVector(sessionId);

  CdmSessionId cdmSessionId(sId.begin(), sId.end());
  if (!mCryptoSessions.count(cdmSessionId)) {
    return Status::ERROR_DRM_SESSION_NOT_OPENED;
  }

  CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];

  if (algo == "HmacSHA256") {
    cryptoSession.setMacAlgorithm(OEMCrypto_HMAC_SHA256);
  } else {
    return Status::ERROR_DRM_CANNOT_HANDLE;
  }

  return Status::OK;
}

Return<void> WVDrmPlugin::encrypt(
    const hidl_vec<uint8_t>& sessionId,
    const hidl_vec<uint8_t>& keyId,
    const hidl_vec<uint8_t>& input,
    const hidl_vec<uint8_t>& iv,
    encrypt_cb _hidl_cb) {

  const std::vector<uint8_t> sId = toVector(sessionId);
  std::vector<uint8_t> output;

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  CdmSessionId cdmSessionId(sId.begin(), sId.end());
  if (!mCryptoSessions.count(cdmSessionId)) {
    _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, toHidlVec(output));
    return Void();
  }

  const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];

  if (cryptoSession.cipherAlgorithm() == kInvalidCryptoAlgorithm) {
    ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
    _hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(output));
    return Void();
  }

  const std::vector<uint8_t> _keyId = toVector(keyId);
  OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
                                           _keyId.data(), _keyId.size());

  if (res != OEMCrypto_SUCCESS) {
    ALOGE("OEMCrypto_SelectKey failed with %u", res);
    _hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
             toHidlVec(output));
    return Void();
  }

  const std::vector<uint8_t> _input = toVector(input);
  const std::vector<uint8_t> _iv = toVector(iv);
  output.resize(_input.size());

  res = mCrypto->encrypt(cryptoSession.oecSessionId(), _input.data(),
                         _input.size(), _iv.data(),
                         cryptoSession.cipherAlgorithm(), output.data());

  if (res == OEMCrypto_SUCCESS) {
    _hidl_cb(Status::OK, toHidlVec(output));
  } else {
    ALOGE("OEMCrypto_Generic_Encrypt failed with %u", res);
    _hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
                      toHidlVec(output));
  }
  return Void();
}

Return<void> WVDrmPlugin::decrypt(
    const hidl_vec<uint8_t>& sessionId,
    const hidl_vec<uint8_t>& keyId,
    const hidl_vec<uint8_t>& input,
    const hidl_vec<uint8_t>& iv,
    decrypt_cb _hidl_cb) {

  const std::vector<uint8_t> sId = toVector(sessionId);
  std::vector<uint8_t> output;

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  CdmSessionId cdmSessionId(sId.begin(), sId.end());
  if (!mCryptoSessions.count(cdmSessionId)) {
    _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, toHidlVec(output));
    return Void();
  }

  const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];

  if (cryptoSession.cipherAlgorithm() == kInvalidCryptoAlgorithm) {
    ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
    _hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(output));
    return Void();
  }

  const std::vector<uint8_t> _keyId = toVector(keyId);
  OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
                                           _keyId.data(), _keyId.size());

  if (res != OEMCrypto_SUCCESS) {
    ALOGE("OEMCrypto_SelectKey failed with %u", res);
    _hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
             toHidlVec(output));
    return Void();
  }

  const std::vector<uint8_t> _input = toVector(input);
  const std::vector<uint8_t> _iv = toVector(iv);
  output.resize(_input.size());

  res = mCrypto->decrypt(cryptoSession.oecSessionId(), _input.data(),
                         _input.size(), _iv.data(),
                         cryptoSession.cipherAlgorithm(), output.data());

  if (res == OEMCrypto_SUCCESS) {
    _hidl_cb(Status::OK, toHidlVec(output));
  } else {
    ALOGE("OEMCrypto_Generic_Decrypt failed with %u", res);
    _hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
             toHidlVec(output));
  }
  return Void();
}

Return<void> WVDrmPlugin::sign(
    const hidl_vec<uint8_t>& sessionId,
    const hidl_vec<uint8_t>& keyId,
    const hidl_vec<uint8_t>& message,
    sign_cb _hidl_cb) {

  const std::vector<uint8_t> sId = toVector(sessionId);
  std::vector<uint8_t> signature;

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  CdmSessionId cdmSessionId(sId.begin(), sId.end());
  if (!mCryptoSessions.count(cdmSessionId)) {
    _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, toHidlVec(signature));
    return Void();
  }

  const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];

  if (cryptoSession.macAlgorithm() == kInvalidCryptoAlgorithm) {
    ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
    _hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(signature));
    return Void();
  }

  const std::vector<uint8_t> _keyId = toVector(keyId);
  OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
                                           _keyId.data(), _keyId.size());

  if (res != OEMCrypto_SUCCESS) {
    ALOGE("OEMCrypto_SelectKey failed with %u", res);
    _hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
                      toHidlVec(signature));
    return Void();
  }

  size_t signatureSize = 0;

  const std::vector<uint8_t> msg = toVector(message);
  res = mCrypto->sign(cryptoSession.oecSessionId(), msg.data(),
                      msg.size(), cryptoSession.macAlgorithm(),
                      NULL, &signatureSize);

  if (res != OEMCrypto_ERROR_SHORT_BUFFER) {
    ALOGE("OEMCrypto_Generic_Sign failed with %u when requesting signature "
          "size", res);
    if (res != OEMCrypto_SUCCESS) {
      _hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
                        toHidlVec(signature));
    } else {
      _hidl_cb(Status::ERROR_DRM_UNKNOWN, toHidlVec(signature));
    }
    return Void();
  }

  signature.resize(signatureSize);

  res = mCrypto->sign(cryptoSession.oecSessionId(), msg.data(),
                      msg.size(), cryptoSession.macAlgorithm(),
                      signature.data(), &signatureSize);

  if (res == OEMCrypto_SUCCESS) {
    _hidl_cb(Status::OK, toHidlVec(signature));
  } else {
    ALOGE("OEMCrypto_Generic_Sign failed with %u", res);
    _hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res),
             toHidlVec(signature));
  }
  return Void();
}

Return<void> WVDrmPlugin::verify(
    const hidl_vec<uint8_t>& sessionId,
    const hidl_vec<uint8_t>& keyId,
    const hidl_vec<uint8_t>& message,
    const hidl_vec<uint8_t>& signature,
    verify_cb _hidl_cb) {

  bool match = false;
  const std::vector<uint8_t> sId = toVector(sessionId);

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  CdmSessionId cdmSessionId(sId.begin(), sId.end());
  if (!mCryptoSessions.count(cdmSessionId)) {
    _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, match);
    return Void();
  }

  const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];

  if (cryptoSession.macAlgorithm() == kInvalidCryptoAlgorithm) {
    ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
    _hidl_cb(Status::ERROR_DRM_UNKNOWN, match);
    return Void();
  }

  const std::vector<uint8_t> _keyId = toVector(keyId);
  OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(),
                                           _keyId.data(), _keyId.size());

  if (res != OEMCrypto_SUCCESS) {
    ALOGE("OEMCrypto_SelectKey failed with %u", res);
    _hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res), match);
    return Void();
  }

  const std::vector<uint8_t> _message = toVector(message);
  const std::vector<uint8_t> _signature = toVector(signature);
  res = mCrypto->verify(cryptoSession.oecSessionId(), _message.data(),
                        _message.size(), cryptoSession.macAlgorithm(),
                        _signature.data(), _signature.size());

  if (res == OEMCrypto_SUCCESS) {
    match = true;
    _hidl_cb(Status::OK, match);
  } else if (res == OEMCrypto_ERROR_SIGNATURE_FAILURE) {
    match = false;
    _hidl_cb(Status::OK, match);
  } else {
    ALOGE("OEMCrypto_Generic_Verify failed with %u", res);
    _hidl_cb(mapAndNotifyOfOEMCryptoResult(sId, res), match);
  }
  return Void();
}

Return<void> WVDrmPlugin::signRSA(
    const hidl_vec<uint8_t>& sessionId,
    const hidl_string& algorithm,
    const hidl_vec<uint8_t>& message,
    const hidl_vec<uint8_t>& wrappedKey,
    signRSA_cb _hidl_cb) {

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (sessionId.size() == 0 || algorithm.size() == 0 ||
      message.size() == 0 || wrappedKey.size() == 0) {
    _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>());
    return Void();
  }
  const std::string algo(algorithm.c_str());
  std::vector<uint8_t> signature;

  RSA_Padding_Scheme padding_scheme;
  if (algo == "RSASSA-PSS-SHA1") {
    padding_scheme = kSign_RSASSA_PSS;
  } else if (algo == "PKCS1-BlockType1") {
    padding_scheme = kSign_PKCS1_Block1;
  } else {
    ALOGE("Unknown RSA Algorithm %s", algo.c_str());
    _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, toHidlVec(signature));
    return Void();
  }

  const std::vector<uint8_t>  msg = toVector(message);
  const std::vector<uint8_t>  _wrappedKey = toVector(wrappedKey);
  OEMCryptoResult res = mCrypto->signRSA(_wrappedKey.data(),
                                         _wrappedKey.size(),
                                         msg.data(), msg.size(),
                                         signature,
                                         padding_scheme);

  if (res != OEMCrypto_SUCCESS) {
    ALOGE("OEMCrypto_GenerateRSASignature failed with %u", res);
    _hidl_cb(mapOEMCryptoResult(res), toHidlVec(signature));
    return Void();
  }

  _hidl_cb(Status::OK, toHidlVec(signature));
  return Void();
}

Return<void> WVDrmPlugin::setListener(const sp<IDrmPluginListener>& listener) {
  mListener = listener;
  mListenerV1_2 = IDrmPluginListener_V1_2::castFrom(listener);
  return Void();
}

Return<void> WVDrmPlugin::sendEvent(
    EventType eventType,
    const hidl_vec<uint8_t>& sessionId,
    const hidl_vec<uint8_t>& data) {
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (mListenerV1_2 != NULL) {
    mListenerV1_2->sendEvent(eventType, sessionId, data);
  } else if (mListener != NULL) {
    mListener->sendEvent(eventType, sessionId, data);
  } else {
    ALOGE("Null event listener, event not sent");
  }
  return Void();
}

Return<void> WVDrmPlugin::sendExpirationUpdate(
    const hidl_vec<uint8_t>& sessionId,
        int64_t expiryTimeInMS) {
  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (mListenerV1_2 != NULL) {
    mListenerV1_2->sendExpirationUpdate(sessionId, expiryTimeInMS);
  } else if (mListener != NULL) {
      mListener->sendExpirationUpdate(sessionId, expiryTimeInMS);
  } else {
    ALOGE("Null event listener, event not sent");
  }
  return Void();
}

template<>
void WVDrmPlugin::_sendKeysChange(
      const hidl_vec<uint8_t>& sessionId,
      const hidl_vec<KeyStatus>& keyStatusList,
      bool hasNewUsableKey) {
  mListener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
}

template<>
void WVDrmPlugin::_sendKeysChange(
      const hidl_vec<uint8_t>& sessionId,
      const hidl_vec<KeyStatus_V1_2>& keyStatusList,
      bool hasNewUsableKey) {
  mListenerV1_2->sendKeysChange_1_2(sessionId, keyStatusList, hasNewUsableKey);
}

Return<void> WVDrmPlugin::sendKeysChange(
     const hidl_vec<uint8_t>& sessionId,
     const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) {
  if (mListenerV1_2 != NULL) {
      mListenerV1_2->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
  } else if (mListener != NULL) {
      mListener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
  } else {
    ALOGE("Null event listener, event not sent");
  }
  return Void();
}

Return<void> WVDrmPlugin::sendKeysChange_1_2(
     const hidl_vec<uint8_t>& sessionId,
     const hidl_vec<KeyStatus_V1_2>& keyStatusList, bool hasNewUsableKey) {
  if (mListenerV1_2 != NULL) {
      mListenerV1_2->sendKeysChange_1_2(sessionId, keyStatusList, hasNewUsableKey);
  } else {
    ALOGE("Null event listener, event not sent");
  }
  return Void();
}

Return<void> WVDrmPlugin::sendSessionLostState(
    const hidl_vec<uint8_t>& sessionId) {
  if (mListenerV1_2 != NULL) {
    mListenerV1_2->sendSessionLostState(sessionId);
  } else {
    ALOGE("Null event listener, event not sent");
  }
  return Void();
}

void WVDrmPlugin::OnSessionRenewalNeeded(const CdmSessionId& cdmSessionId) {
  const std::vector<uint8_t> sessionId = StrToVector(cdmSessionId);
  const hidl_vec<uint8_t> data;  // data is ignored
  const hidl_vec<uint8_t> sid = toHidlVec(sessionId);
  sendEvent(EventType::KEY_NEEDED, sid, data);
}

void WVDrmPlugin::OnSessionKeysChange(const CdmSessionId& cdmSessionId,
                                      const CdmKeyStatusMap& cdmKeysStatus,
                                      bool hasNewUsableKey) {
  if (mListenerV1_2 != NULL) {
    _OnSessionKeysChange<KeyStatus_V1_2>(cdmSessionId, cdmKeysStatus, hasNewUsableKey);
  } else if (mListener != NULL) {
    _OnSessionKeysChange<KeyStatus>(cdmSessionId, cdmKeysStatus, hasNewUsableKey);
  } else {
    ALOGE("Null event listener, event not sent");
  }
}

template<typename KS>
void WVDrmPlugin::_OnSessionKeysChange(const CdmSessionId& cdmSessionId,
                                       const CdmKeyStatusMap& cdmKeysStatus,
                                       bool hasNewUsableKey) {
  bool expired = false;
  std::vector<KS> keyStatusList;
  for (CdmKeyStatusMap::const_iterator it = cdmKeysStatus.begin();
       it != cdmKeysStatus.end(); ++it) {
    const KeyId& keyId = it->first;
    const CdmKeyStatus cdmKeyStatus = it->second;
    if (cdmKeyStatus == wvcdm::kKeyStatusExpired) expired = true;

    KS keyStatus;
    keyStatus.keyId = toHidlVec(StrToVector(keyId));
    keyStatus.type = ConvertFromCdmKeyStatus<decltype(keyStatus.type)>(cdmKeyStatus);
    keyStatusList.push_back(keyStatus);
  }

  const std::vector<uint8_t> sessionId = StrToVector(cdmSessionId);
  const hidl_vec<uint8_t> data;  // data is ignored
  const hidl_vec<uint8_t> sid = toHidlVec(sessionId);
  _sendKeysChange<KS>(sid, toHidlVec(keyStatusList), hasNewUsableKey);
  // For backward compatibility.
  if (expired) {
    sendEvent(EventType::KEY_EXPIRED, sid, data);
  }
}

void WVDrmPlugin::OnExpirationUpdate(const CdmSessionId& cdmSessionId,
                                     int64_t newExpiryTimeSeconds) {
  const std::vector<uint8_t> sessionId = StrToVector(cdmSessionId);
  int64_t newExpiryTimeMilliseconds =
      newExpiryTimeSeconds == wvcdm::NEVER_EXPIRES
      ? newExpiryTimeSeconds : newExpiryTimeSeconds * 1000;

  sendExpirationUpdate(toHidlVec(sessionId), newExpiryTimeMilliseconds);
}

void WVDrmPlugin::OnSessionLostState(const CdmSessionId& cdmSessionId) {
  const std::vector<uint8_t> sessionId = StrToVector(cdmSessionId);
  sendSessionLostState(toHidlVec(sessionId));
}

Status WVDrmPlugin::queryProperty(const std::string& property,
                                    std::string& stringValue) const {
  wvcdm::SecurityLevel securityLevel =
      mPropertySet.security_level().compare(
          wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0
          ? wvcdm::kLevel3
          : wvcdm::kLevelDefault;
  return queryProperty(securityLevel, property, stringValue);
}

Status WVDrmPlugin::queryProperty(wvcdm::SecurityLevel securityLevel,
                                    const std::string& property,
                                    std::string& stringValue) const {
  CdmResponseType res =
      mCDM->QueryStatus(securityLevel, property, &stringValue);

  if (res != wvcdm::NO_ERROR) {
    ALOGE("Error querying CDM status: %u", res);
  }
  return mapCdmResponseType(res);
}

Status WVDrmPlugin::queryProperty(const std::string& property,
                                    std::vector<uint8_t>& vector_value) const {
  std::string string_value;
  Status status = queryProperty(property, string_value);
  if (status != Status::OK) return status;
  vector_value = StrToVector(string_value);
  return Status::OK;
}

Status WVDrmPlugin::mapAndNotifyOfCdmResponseType(
    const std::vector<uint8_t>& sessionId, CdmResponseType res) {
  notifyOfCdmResponseType(sessionId, res);
  return mapCdmResponseType(res);
}

Status_V1_2 WVDrmPlugin::mapAndNotifyOfCdmResponseType_1_2(
    const std::vector<uint8_t>& sessionId, CdmResponseType res) {
  notifyOfCdmResponseType(sessionId, res);
  return mapCdmResponseType_1_2(res);
}

void WVDrmPlugin::notifyOfCdmResponseType(
    const std::vector<uint8_t>& sessionId,
    CdmResponseType res) {

  const hidl_vec<uint8_t> data;  // data is ignored
  if (res == wvcdm::NEED_PROVISIONING) {
    sendEvent(EventType::PROVISION_REQUIRED, toHidlVec(sessionId), data);
  } else if (res == wvcdm::NEED_KEY) {
    sendEvent(EventType::KEY_NEEDED, toHidlVec(sessionId), data);
  }
}

Status WVDrmPlugin::mapAndNotifyOfOEMCryptoResult(
    const std::vector<uint8_t>& sessionId,
    OEMCryptoResult res) {

  const hidl_vec<uint8_t> data;  // data is ignored
  if (res == OEMCrypto_ERROR_NO_DEVICE_KEY) {
    sendEvent(EventType::PROVISION_REQUIRED, toHidlVec(sessionId), data);
  }
  return mapOEMCryptoResult(res);
}

Status WVDrmPlugin::mapOEMCryptoResult(OEMCryptoResult res) {
  switch (res) {
    case OEMCrypto_SUCCESS:
      return Status::OK;

    case OEMCrypto_ERROR_SIGNATURE_FAILURE:
      return Status::ERROR_DRM_INVALID_STATE;

    case OEMCrypto_ERROR_NO_DEVICE_KEY:
      return Status::ERROR_DRM_NOT_PROVISIONED;

    case OEMCrypto_ERROR_INVALID_SESSION:
      return Status::ERROR_DRM_SESSION_NOT_OPENED;

    case OEMCrypto_ERROR_TOO_MANY_SESSIONS:
    case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
      return Status::ERROR_DRM_RESOURCE_BUSY;

    case OEMCrypto_ERROR_NOT_IMPLEMENTED:
      return Status::ERROR_DRM_CANNOT_HANDLE;

    case OEMCrypto_ERROR_INVALID_RSA_KEY:
    case OEMCrypto_ERROR_SHORT_BUFFER:
    case OEMCrypto_ERROR_UNKNOWN_FAILURE:
    case OEMCrypto_ERROR_OPEN_SESSION_FAILED:
      FALLTHROUGH_INTENDED; /* FALLTHROUGH */
    default:
      ALOGW("Returns UNKNOWN error for legacy status: %d", res);
      return Status::ERROR_DRM_UNKNOWN;
  }
}

bool WVDrmPlugin::initDataResemblesPSSH(const std::vector<uint8_t>& initData) {
  const uint8_t* const initDataArray = initData.data();

  ALOGE("[BTVV-4467] %s, ", __FUNCTION__);
  if (sizeof(uint32_t) + kPsshTag.size() > initData.size()) {
    // The init data is so small that it couldn't contain a size and PSSH tag.
    return false;
  }

  // Extract the size field
  const uint8_t* const sizeField = &initDataArray[0];
  uint32_t nboSize;
  memcpy(&nboSize, sizeField, sizeof(nboSize));
  uint32_t size = ntohl(nboSize);

  if (size > initData.size()) {
    return false;
  }

  // Extract the ID field
  const char* const idField =
      reinterpret_cast<const char* const>(&initDataArray[sizeof(nboSize)]);
  std::string id(idField, kPsshTag.size());
  return id == kPsshTag;
}

Status WVDrmPlugin::unprovision(const CdmIdentifier& identifier) {
  CdmResponseType res1 = mCDM->Unprovision(wvcdm::kSecurityLevelL1, identifier);
  CdmResponseType res3 = mCDM->Unprovision(wvcdm::kSecurityLevelL3, identifier);
  if (!isCdmResponseTypeSuccess(res1))
  {
    return mapCdmResponseType(res1);
  }
  else
  {
    return mapCdmResponseType(res3);
  }
}

// Implementation for the CdmIdentifierBuilder inner class
WVDrmPlugin::CdmIdentifierBuilder::CdmIdentifierBuilder(
    bool useSpoid, const WVDrmPlugin& parent, const std::string& appPackageName)
  : mCdmIdentifier(),
    mIsIdentifierSealed(false),
    mUseSpoid(useSpoid),
    mAppPackageName(appPackageName),
    mParent(parent) {
  mCdmIdentifier.app_package_name = mAppPackageName;
  mCdmIdentifier.unique_id = getNextUniqueId();
}

Status WVDrmPlugin::CdmIdentifierBuilder::getCdmIdentifier(
    CdmIdentifier* identifier) {
  if (!mIsIdentifierSealed) {
    Status res = calculateSpoid();
    if (res != Status::OK) return res;
    mIsIdentifierSealed = true;
  }
  *identifier = mCdmIdentifier;
  return Status::OK;
}

Status WVDrmPlugin::CdmIdentifierBuilder::getDeviceUniqueId(std::string* id) {
  if (mUseSpoid) {
    CdmIdentifier identifier;
    Status res = getCdmIdentifier(&identifier);
    if (res != Status::OK) return res;

    *id = identifier.spoid;
    return Status::OK;
  } else {
    return getOemcryptoDeviceId(id);
  }
}

Status WVDrmPlugin::CdmIdentifierBuilder::getProvisioningUniqueId(std::string* id) {
  if (mUseSpoid) {
    // To fake a provisioning-unique ID on SPOID devices where we can't expose
    // the real provisioning-unique ID, we just use the SPOID and invert all the
    // bits.
    Status res = getDeviceUniqueId(id);
    if (res != Status::OK) return res;

    for (char& c : *id) {
      c = ~c;
    }

    return Status::OK;
  } else {
    return mParent.queryProperty(wvcdm::QUERY_KEY_PROVISIONING_ID, *id);
  }
}

bool WVDrmPlugin::CdmIdentifierBuilder::set_origin(const std::string& id) {
  if (mIsIdentifierSealed) return false;
  mCdmIdentifier.origin = id;
  return true;
}

Status WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid() {
  if (mUseSpoid) {
    std::string deviceId;
    Status res = getOemcryptoDeviceId(&deviceId);
    if (res != Status::OK) return res;

    uint8_t hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX ctx;
    SHA256_Init(&ctx);
    SHA256_Update(&ctx, deviceId.data(), deviceId.length());
    SHA256_Update(&ctx, mAppPackageName.data(), mAppPackageName.length());
    SHA256_Update(&ctx, origin().data(), origin().length());
    SHA256_Final(hash, &ctx);

    mCdmIdentifier.spoid =
        std::string(reinterpret_cast<char*>(hash), SHA256_DIGEST_LENGTH);
  }
  return Status::OK;
}

Status WVDrmPlugin::CdmIdentifierBuilder::getOemcryptoDeviceId(
    std::string* id) {
  return mParent.queryProperty(wvcdm::QUERY_KEY_DEVICE_ID, *id);
}

uint32_t WVDrmPlugin::CdmIdentifierBuilder::getNextUniqueId() {
  // Start with 1. 0 is reserved for the default cdm identifier.
  static uint32_t unique_id = 1;
  return ++unique_id;
}

}  // namespace widevine
}  // namespace V1_2
}  // namespace drm
}  // namespace hardware
}  // namespace wvdrm
WVDrmPlugin.cpp (72,445 bytes)   

(ALTech) Younkwang Jung

2022-07-07 10:47

developer   ~0010326

HI Kerwin

The code you added to plugin is ok, SKB said.
and SKB asked me to add logs to drmserver as well.

Additional request from SKB are as follows.
The DRM structure is as follows:
drm client <- AIDL -> drmserver <-HIDL-> drm plugin

ANR(issue) occurred in drm client.
you already put the debug log at drm plugin
so SKB is requesting to add logs to drmserver as well.

please add debug log to the drmserver.
frameworks/av/drm/drmserver

Thank you
YK.Jung

(SW) Kerwin Chen

2022-07-08 14:27

developer   ~0010335

Hi YK,

Modified file is attached here.
I add debug log in 'DrmManager.cpp' only because it is real implementations for 'DrmManagerService.cpp'.
Thank you !
DrmManager.cpp (27,107 bytes)   
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "DrmManager(Native)"
#include "utils/Log.h"

#include <utils/String8.h>
#include <drm/DrmInfo.h>
#include <drm/DrmInfoEvent.h>
#include <drm/DrmRights.h>
#include <drm/DrmConstraints.h>
#include <drm/DrmMetadata.h>
#include <drm/DrmInfoStatus.h>
#include <drm/DrmInfoRequest.h>
#include <drm/DrmSupportInfo.h>
#include <drm/DrmConvertedStatus.h>
#include <IDrmEngine.h>

#include "DrmManager.h"
#include "ReadWriteUtils.h"

#define DECRYPT_FILE_ERROR (-1)

using namespace android;

const String8 DrmManager::EMPTY_STRING("");

DrmManager::DrmManager() :
    mDecryptSessionId(0),
    mConvertId(0) {
    srand(time(NULL));
    memset(mUniqueIdArray, 0, sizeof(bool) * kMaxNumUniqueIds);
    ALOGE("[BTVV-4467] %s, %d\n", __FUNCTION__, __LINE__);
}

DrmManager::~DrmManager() {
    ALOGE("%s, %d\n", __FUNCTION__, __LINE__);
}

int DrmManager::addUniqueId(bool isNative) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);

    int uniqueId = -1;
    int random = rand();

    for (size_t index = 0; index < kMaxNumUniqueIds; ++index) {
        int temp = (random + index) % kMaxNumUniqueIds;
        if (!mUniqueIdArray[temp]) {
            uniqueId = temp;
            mUniqueIdArray[uniqueId] = true;

            if (isNative) {
                // set a flag to differentiate DrmManagerClient
                // created from native side and java side
                uniqueId |= 0x1000;
            }
            break;
        }
    }

    // -1 indicates that no unique id can be allocated.
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return uniqueId;
}

void DrmManager::removeUniqueId(int uniqueId) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    if (uniqueId & 0x1000) {
        // clear the flag for the native side.
        uniqueId &= ~(0x1000);
    }

    if (uniqueId >= 0 && uniqueId < kMaxNumUniqueIds) {
        mUniqueIdArray[uniqueId] = false;
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
}

status_t DrmManager::loadPlugIns() {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    String8 pluginDirPath("/system/lib/drm");
    loadPlugIns(pluginDirPath);
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return DRM_NO_ERROR;
}

status_t DrmManager::loadPlugIns(const String8& plugInDirPath) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    mPlugInManager.loadPlugIns(plugInDirPath);
    Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
    for (size_t i = 0; i < plugInPathList.size(); ++i) {
        String8 plugInPath = plugInPathList[i];
        DrmSupportInfo* info = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0);
        if (NULL != info) {
            if (mSupportInfoToPlugInIdMap.indexOfKey(*info) < 0) {
                mSupportInfoToPlugInIdMap.add(*info, plugInPath);
            }
            delete info;
        }
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return DRM_NO_ERROR;
}

status_t DrmManager::unloadPlugIns() {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    mConvertSessionMap.clear();
    mDecryptSessionMap.clear();
    mPlugInManager.unloadPlugIns();
    mSupportInfoToPlugInIdMap.clear();
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return DRM_NO_ERROR;
}

status_t DrmManager::setDrmServiceListener(
            int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mListenerLock);
    if (NULL != drmServiceListener.get()) {
        mServiceListeners.add(uniqueId, drmServiceListener);
    } else {
        mServiceListeners.removeItem(uniqueId);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return DRM_NO_ERROR;
}

void DrmManager::addClient(int uniqueId) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    if (!mSupportInfoToPlugInIdMap.isEmpty()) {
        Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
        for (size_t index = 0; index < plugInIdList.size(); index++) {
            IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
            rDrmEngine.initialize(uniqueId);
            rDrmEngine.setOnInfoListener(uniqueId, this);
        }
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
}

void DrmManager::removeClient(int uniqueId) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
    for (size_t index = 0; index < plugInIdList.size(); index++) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
        rDrmEngine.terminate(uniqueId);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
}

DrmConstraints* DrmManager::getConstraints(int uniqueId, const String8* path, const int action) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
    if (EMPTY_STRING != plugInId) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
        ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
        return rDrmEngine.getConstraints(uniqueId, path, action);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return NULL;
}

DrmMetadata* DrmManager::getMetadata(int uniqueId, const String8* path) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
    if (EMPTY_STRING != plugInId) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
        ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
        return rDrmEngine.getMetadata(uniqueId, path);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return NULL;
}

bool DrmManager::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    const String8 plugInId = getSupportedPlugInId(mimeType);
    bool result = (EMPTY_STRING != plugInId) ? true : false;

    if (0 < path.length()) {
        if (result) {
            IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
            result = rDrmEngine.canHandle(uniqueId, path);
        } else {
            String8 extension = path.getPathExtension();
            if (String8("") != extension) {
                result = canHandle(uniqueId, path);
            }
        }
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

DrmInfoStatus* DrmManager::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    const String8 plugInId = getSupportedPlugInId(drmInfo->getMimeType());
    if (EMPTY_STRING != plugInId) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
        ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
        return rDrmEngine.processDrmInfo(uniqueId, drmInfo);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return NULL;
}

bool DrmManager::canHandle(int uniqueId, const String8& path) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    bool result = false;
    Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();

    for (size_t i = 0; i < plugInPathList.size(); ++i) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInPathList[i]);
        result = rDrmEngine.canHandle(uniqueId, path);

        if (result) {
            break;
        }
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

DrmInfo* DrmManager::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    const String8 plugInId = getSupportedPlugInId(drmInfoRequest->getMimeType());
    if (EMPTY_STRING != plugInId) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
        ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
        return rDrmEngine.acquireDrmInfo(uniqueId, drmInfoRequest);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return NULL;
}

status_t DrmManager::saveRights(int uniqueId, const DrmRights& drmRights,
            const String8& rightsPath, const String8& contentPath) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    const String8 plugInId = getSupportedPlugInId(drmRights.getMimeType());
    status_t result = DRM_ERROR_UNKNOWN;
    if (EMPTY_STRING != plugInId) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
        result = rDrmEngine.saveRights(uniqueId, drmRights, rightsPath, contentPath);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

String8 DrmManager::getOriginalMimeType(int uniqueId, const String8& path, int fd) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
    if (EMPTY_STRING != plugInId) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
        ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
        return rDrmEngine.getOriginalMimeType(uniqueId, path, fd);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return EMPTY_STRING;
}

int DrmManager::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    const String8 plugInId = getSupportedPlugInId(uniqueId, path, mimeType);
    if (EMPTY_STRING != plugInId) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
        ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
        return rDrmEngine.getDrmObjectType(uniqueId, path, mimeType);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return DrmObjectType::UNKNOWN;
}

int DrmManager::checkRightsStatus(int uniqueId, const String8& path, int action) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
    if (EMPTY_STRING != plugInId) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
        ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
        return rDrmEngine.checkRightsStatus(uniqueId, path, action);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return RightsStatus::RIGHTS_INVALID;
}

status_t DrmManager::consumeRights(
    int uniqueId, sp<DecryptHandle>& decryptHandle, int action, bool reserve) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    status_t result = DRM_ERROR_UNKNOWN;
    Mutex::Autolock _l(mDecryptLock);
    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
        result = drmEngine->consumeRights(uniqueId, decryptHandle, action, reserve);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

status_t DrmManager::setPlaybackStatus(
    int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus, int64_t position) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    status_t result = DRM_ERROR_UNKNOWN;
    Mutex::Autolock _l(mDecryptLock);
    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
        result = drmEngine->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

bool DrmManager::validateAction(
    int uniqueId, const String8& path, int action, const ActionDescription& description) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
    if (EMPTY_STRING != plugInId) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
        ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
        return rDrmEngine.validateAction(uniqueId, path, action, description);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return false;
}

status_t DrmManager::removeRights(int uniqueId, const String8& path) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
    status_t result = DRM_ERROR_UNKNOWN;
    if (EMPTY_STRING != plugInId) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
        result = rDrmEngine.removeRights(uniqueId, path);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

status_t DrmManager::removeAllRights(int uniqueId) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
    status_t result = DRM_ERROR_UNKNOWN;
    for (size_t index = 0; index < plugInIdList.size(); index++) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
        result = rDrmEngine.removeAllRights(uniqueId);
        if (DRM_NO_ERROR != result) {
            break;
        }
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

int DrmManager::openConvertSession(int uniqueId, const String8& mimeType) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mConvertLock);
    int convertId = -1;

    const String8 plugInId = getSupportedPlugInId(mimeType);
    if (EMPTY_STRING != plugInId) {
        IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);

        if (DRM_NO_ERROR == rDrmEngine.openConvertSession(uniqueId, mConvertId + 1)) {
            ++mConvertId;
            convertId = mConvertId;
            mConvertSessionMap.add(convertId, &rDrmEngine);
        }
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return convertId;
}

DrmConvertedStatus* DrmManager::convertData(
            int uniqueId, int convertId, const DrmBuffer* inputData) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    DrmConvertedStatus *drmConvertedStatus = NULL;

    Mutex::Autolock _l(mConvertLock);
    if (mConvertSessionMap.indexOfKey(convertId) != NAME_NOT_FOUND) {
        IDrmEngine* drmEngine = mConvertSessionMap.valueFor(convertId);
        drmConvertedStatus = drmEngine->convertData(uniqueId, convertId, inputData);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return drmConvertedStatus;
}

DrmConvertedStatus* DrmManager::closeConvertSession(int uniqueId, int convertId) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mConvertLock);
    DrmConvertedStatus *drmConvertedStatus = NULL;

    if (mConvertSessionMap.indexOfKey(convertId) != NAME_NOT_FOUND) {
        IDrmEngine* drmEngine = mConvertSessionMap.valueFor(convertId);
        drmConvertedStatus = drmEngine->closeConvertSession(uniqueId, convertId);
        mConvertSessionMap.removeItem(convertId);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return drmConvertedStatus;
}

status_t DrmManager::getAllSupportInfo(
                    int /* uniqueId */, int* length, DrmSupportInfo** drmSupportInfoArray) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mLock);
    Vector<String8> plugInPathList = mPlugInManager.getPlugInIdList();
    int size = plugInPathList.size();
    int validPlugins = 0;

    if (0 < size) {
        Vector<DrmSupportInfo> drmSupportInfoList;

        for (int i = 0; i < size; ++i) {
            String8 plugInPath = plugInPathList[i];
            DrmSupportInfo* drmSupportInfo
                = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0);
            if (NULL != drmSupportInfo) {
                drmSupportInfoList.add(*drmSupportInfo);
                delete drmSupportInfo; drmSupportInfo = NULL;
            }
        }

        validPlugins = drmSupportInfoList.size();
        if (0 < validPlugins) {
            *drmSupportInfoArray = new DrmSupportInfo[validPlugins];
            for (int i = 0; i < validPlugins; ++i) {
                (*drmSupportInfoArray)[i] = drmSupportInfoList[i];
            }
        }
    }
    *length = validPlugins;
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return DRM_NO_ERROR;
}

sp<DecryptHandle> DrmManager::openDecryptSession(
        int uniqueId, int fd, off64_t offset, off64_t length, const char* mime) {

    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mDecryptLock);
    status_t result = DRM_ERROR_CANNOT_HANDLE;
    Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();

    sp<DecryptHandle> handle = new DecryptHandle();
    if (NULL != handle.get()) {
        handle->decryptId = mDecryptSessionId + 1;

        for (size_t index = 0; index < plugInIdList.size(); index++) {
            const String8& plugInId = plugInIdList.itemAt(index);
            IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
            result = rDrmEngine.openDecryptSession(uniqueId, handle, fd, offset, length, mime);

            if (DRM_NO_ERROR == result) {
                ++mDecryptSessionId;
                mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
                break;
            }
        }
    }
    if (DRM_NO_ERROR != result) {
        handle.clear();
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return handle;
}

sp<DecryptHandle> DrmManager::openDecryptSession(
        int uniqueId, const char* uri, const char* mime) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mDecryptLock);
    status_t result = DRM_ERROR_CANNOT_HANDLE;
    Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();

    sp<DecryptHandle> handle = new DecryptHandle();
    if (NULL != handle.get()) {
        handle->decryptId = mDecryptSessionId + 1;

        for (size_t index = 0; index < plugInIdList.size(); index++) {
            const String8& plugInId = plugInIdList.itemAt(index);
            IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
            result = rDrmEngine.openDecryptSession(uniqueId, handle, uri, mime);

            if (DRM_NO_ERROR == result) {
                ++mDecryptSessionId;
                mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
                break;
            }
        }
    }
    if (DRM_NO_ERROR != result) {
        handle.clear();
        ALOGV("DrmManager::openDecryptSession: no capable plug-in found");
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return handle;
}

sp<DecryptHandle> DrmManager::openDecryptSession(
        int uniqueId, const DrmBuffer& buf, const String8& mimeType) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mDecryptLock);
    status_t result = DRM_ERROR_CANNOT_HANDLE;
    Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();

    sp<DecryptHandle> handle = new DecryptHandle();
    if (NULL != handle.get()) {
        handle->decryptId = mDecryptSessionId + 1;

        for (size_t index = 0; index < plugInIdList.size(); index++) {
            const String8& plugInId = plugInIdList.itemAt(index);
            IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
            result = rDrmEngine.openDecryptSession(uniqueId, handle, buf, mimeType);

            if (DRM_NO_ERROR == result) {
                ++mDecryptSessionId;
                mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
                break;
            }
        }
    }
    if (DRM_NO_ERROR != result) {
        handle.clear();
        ALOGV("DrmManager::openDecryptSession: no capable plug-in found");
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return handle;
}

status_t DrmManager::closeDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mDecryptLock);
    status_t result = DRM_ERROR_UNKNOWN;
    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
        result = drmEngine->closeDecryptSession(uniqueId, decryptHandle);
        if (DRM_NO_ERROR == result && NULL != decryptHandle.get()) {
            mDecryptSessionMap.removeItem(decryptHandle->decryptId);
        }
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

status_t DrmManager::initializeDecryptUnit(
        int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
        const DrmBuffer* headerInfo) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    status_t result = DRM_ERROR_UNKNOWN;
    Mutex::Autolock _l(mDecryptLock);
    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
        result = drmEngine->initializeDecryptUnit(uniqueId, decryptHandle, decryptUnitId, headerInfo);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

status_t DrmManager::decrypt(int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
            const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    status_t result = DRM_ERROR_UNKNOWN;

    Mutex::Autolock _l(mDecryptLock);
    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
        result = drmEngine->decrypt(
                uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

status_t DrmManager::finalizeDecryptUnit(
            int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    status_t result = DRM_ERROR_UNKNOWN;
    Mutex::Autolock _l(mDecryptLock);
    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
        result = drmEngine->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

ssize_t DrmManager::pread(int uniqueId, sp<DecryptHandle>& decryptHandle,
            void* buffer, ssize_t numBytes, off64_t offset) {
    ssize_t result = DECRYPT_FILE_ERROR;

    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mDecryptLock);
    if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
        IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
        result = drmEngine->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return result;
}

String8 DrmManager::getSupportedPlugInId(
            int uniqueId, const String8& path, const String8& mimeType) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    String8 plugInId("");

    if (EMPTY_STRING != mimeType) {
        plugInId = getSupportedPlugInId(mimeType);
    } else {
        plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return plugInId;
}

String8 DrmManager::getSupportedPlugInId(const String8& mimeType) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    String8 plugInId("");

    if (EMPTY_STRING != mimeType) {
        for (size_t index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) {
            const DrmSupportInfo& drmSupportInfo = mSupportInfoToPlugInIdMap.keyAt(index);

            if (drmSupportInfo.isSupportedMimeType(mimeType)) {
                plugInId = mSupportInfoToPlugInIdMap.valueFor(drmSupportInfo);
                break;
            }
        }
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return plugInId;
}

String8 DrmManager::getSupportedPlugInIdFromPath(int uniqueId, const String8& path) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    String8 plugInId("");
    const String8 fileSuffix = path.getPathExtension();

    for (size_t index = 0; index < mSupportInfoToPlugInIdMap.size(); index++) {
        const DrmSupportInfo& drmSupportInfo = mSupportInfoToPlugInIdMap.keyAt(index);

        if (drmSupportInfo.isSupportedFileSuffix(fileSuffix)) {
            String8 key = mSupportInfoToPlugInIdMap.valueFor(drmSupportInfo);
            IDrmEngine& drmEngine = mPlugInManager.getPlugIn(key);

            if (drmEngine.canHandle(uniqueId, path)) {
                plugInId = key;
                break;
            }
        }
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
    return plugInId;
}

void DrmManager::onInfo(const DrmInfoEvent& event) {
    ALOGE("[BTVV-4467] Entering %s\n", __FUNCTION__);
    Mutex::Autolock _l(mListenerLock);
    for (size_t index = 0; index < mServiceListeners.size(); index++) {
        int uniqueId = mServiceListeners.keyAt(index);

        if (uniqueId == event.getUniqueId()) {
            sp<IDrmServiceListener> serviceListener = mServiceListeners.valueFor(uniqueId);
            serviceListener->notify(event);
        }
    }
    ALOGE("[BTVV-4467] Leaving %s, %d\n", __FUNCTION__, __LINE__);
}

DrmManager.cpp (27,107 bytes)   

(ALTech) Younkwang Jung

2022-07-12 07:26

developer   ~0010346

Hi Kerwin

Please commit the files that you added the log to the Smart3
- WVGenericCryptoInterface.cpp
- WVDrmPlugin.cpp
- DrmManager.cpp

Thank you
YK.Jung

(SW) Kerwin Chen

2022-07-12 08:54

developer   ~0010347

The changes are committed to BitBucket 5.3.4 branch.

(ALTech) Younkwang Jung

2022-08-29 08:25

developer   ~0010729

Hi Kerwin

This issue will be updated again after observing this log in the field.
I will close this issue.

Thank you
YK.Jung

Issue History

Date Modified Username Field Change
2022-06-13 10:32 (ALTech) Younkwang Jung New Issue
2022-06-13 10:32 (ALTech) Younkwang Jung Status new => assigned
2022-06-13 10:32 (ALTech) Younkwang Jung Assigned To => (SW) Kerwin Chen
2022-06-13 10:36 (ALTech) Younkwang Jung Issue Monitored: (ALTech) SY Yoon
2022-06-13 10:36 (ALTech) Younkwang Jung Issue Monitored: (ALTech) JunGyu Kim
2022-06-13 10:36 (ALTech) Younkwang Jung Issue Monitored: (SW) Jacky Chiang
2022-06-13 10:36 (ALTech) Younkwang Jung Issue Monitored: (SW) Jason Ling
2022-06-13 10:45 (ALTech) Younkwang Jung File Added: ANR_#1.zip
2022-06-13 10:45 (ALTech) Younkwang Jung Note Added: 0010154
2022-06-13 10:48 (ALTech) Younkwang Jung Note Edited: 0010154 View Revisions
2022-06-13 16:44 (ALTech) Younkwang Jung Note Added: 0010162
2022-06-13 18:02 (SW) Kerwin Chen Note Added: 0010163
2022-06-13 18:03 (SW) Kerwin Chen Note Edited: 0010163
2022-06-13 20:25 (SW) Kerwin Chen Note Edited: 0010163
2022-06-30 16:54 (ALTech) Younkwang Jung Note Added: 0010278
2022-07-04 08:53 (SW) Kerwin Chen File Added: WVGenericCryptoInterface.cpp
2022-07-04 08:53 (SW) Kerwin Chen File Added: WVDrmPlugin.cpp
2022-07-04 08:53 (SW) Kerwin Chen Note Added: 0010299
2022-07-07 10:47 (ALTech) Younkwang Jung Note Added: 0010326
2022-07-08 14:27 (SW) Kerwin Chen File Added: DrmManager.cpp
2022-07-08 14:27 (SW) Kerwin Chen Note Added: 0010335
2022-07-12 07:26 (ALTech) Younkwang Jung Note Added: 0010346
2022-07-12 08:54 (SW) Kerwin Chen Assigned To (SW) Kerwin Chen => (ALTech) Younkwang Jung
2022-07-12 08:54 (SW) Kerwin Chen Status assigned => resolved
2022-07-12 08:54 (SW) Kerwin Chen Resolution open => fixed
2022-07-12 08:54 (SW) Kerwin Chen Note Added: 0010347
2022-08-29 08:25 (ALTech) Younkwang Jung Note Added: 0010729
2022-08-29 08:25 (ALTech) Younkwang Jung Status resolved => closed